Я использую одну и ту же связку в двух очень разных мирах. В Lumeo — финансовой платформе для бизнеса — это дашборды, отчётность и данные в реальном времени на восьми языках. В D.I.S.C. — небольшое приложение, которое должно хорошо работать на телефоне участника посреди тренинга. В обоих случаях один код собирается в веб, iOS и Android.
Из чего состоит связка
- Angular — архитектура, роутинг, формы и реактивность. Standalone-компоненты и Signals сделали код заметно проще: меньше модулей, меньше лишних проверок изменений.
- Ionic — набор UI-компонентов, которые выглядят и ведут себя нативно: жесты, модальные окна, списки, переходы.
- Capacitor — мост к нативному слою. Веб-приложение живёт в WebView, а камера, хранилище, вибрация и клавиатура доступны через плагины.
Что это даёт бизнесу
Главное — одна команда и одна логика вместо трёх. Новая функция в платёжном или аналитическом интерфейсе сразу появляется на всех платформах. Ошибка исправляется один раз. Код-ревью, тесты и стандарты — тоже одни на все платформы.
Для собственного продукта эффект ещё сильнее: один человек может довести приложение до магазина, не переписывая его под каждую платформу.
Где нужно внимание
У связки есть слабые места. Вот что я проверяю на реальных устройствах в каждом релизе:
- Безопасные зоны экрана. Статус-бар и вырезы на Android и iOS ведут себя по-разному — шапку приходится подстраивать под отступы системы, иначе кнопки уезжают под часы.
- Системная кнопка «Назад». Аппаратная кнопка Android и свайп назад на iOS могут выкинуть человека из середины анкеты. В D.I.S.C. на время прохождения они отключены — выйти можно только явным действием.
- Хранилище. В браузере —
localStorage, на телефоне — нативные Preferences. Всё это прячется за одним сервисом, чтобы остальной код не знал, где он запущен. - Жизненный цикл. Телефон уходит в фон, сеть пропадает. WebSocket-соединение должно само восстанавливаться при возврате в приложение, а интерфейс — честно показывать состояние.
- Нативные API — в одном месте. Камеру, геолокацию, биометрию и уведомления я вызываю только через единый сервис, а не напрямую из компонентов. Так проще тестировать и проще поддерживать веб-версию, где этих API нет.
Бесплатная версия и Pro в одном приложении
Отдельный приём, который пригодился в D.I.S.C.: бесплатный режим для участника и Pro для ведущего живут в одном приложении. Pro открывается встроенной покупкой в магазине, а сервер подтверждает лицензию. Сборки отличаются только магазином: свой код покупки для RuStore, Google Play и App Store.
Когда я бы выбрал другое
Если продукт — это тяжёлая графика, сложные анимации на 60 кадрах в секунду или глубокая работа с железом, лучше писать нативно. Но для финтеха, бэк-офисов, кабинетов и форм — то есть для большинства бизнес-приложений — одна кодовая база окупается уже на втором релизе.