Лекция 4. Кроссплатформенная разработка

Один код - несколько платформ: подходы, инструменты, компромиссы

План лекции

Основы

  • Зачем нужна кроссплатформенность
  • Спектр переиспользования кода
  • Базовые понятия: рендеринг, мост, JIT/AOT

Семейства технологий

  • PWA и веб-подход
  • Гибридные (WebView) решения
  • Нативный рендеринг (RN, NativeScript)
  • Собственный движок (Flutter)
  • Общая логика (KMP, .NET MAUI)

Инженерные аспекты

  • Производительность и размер
  • Доступ к нативным API
  • Тестирование и CI/CD
  • Безопасность и обновления

Выбор технологии

  • Сводное сравнение
  • Дерево решений
  • Тренды 2026

Контекст

Рынок мобильных ОС

  • Android - около 70-72% мирового рынка
  • iOS - около 28-30%, но в США и ряде стран Западной Европы доля выше 50%
  • HarmonyOS (Huawei) - стремительный рост в Китае, вытесняет Android-без-Google-сервисов
  • Нишевые ОС: KaiOS (кнопочные), Аврора (Россия), Wear OS, tvOS

Фрагментация Android

  • Тысячи моделей устройств: разные экраны, плотности, процессоры
  • Версии ОС: значительная доля пользователей на Android 10-13, свежие версии догоняют годами
  • Вендорские прошивки (One UI, MIUI/HyperOS): свои настройки энергосбережения и уведомлений
  • Следствие: нужно тестировать на реальном "зоопарке" устройств, а не только в эмуляторе

iOS-фрагментация почти отсутствует: новые версии принимаются быстро, моделей мало.

Проблема двух кодовых баз

  • Две команды: Kotlin/Java и Swift/Objective-C
  • Двойной объем тестирования, релизов, документации
  • Функция, разработанная "за месяц", выходит на второй платформе позже
  • Рост стоимости поддержки (TCO) со временем нелинейный
  • Сложнее удерживать единообразие дизайна и логики

Идея кроссплатформенности: написать один раз - запускать везде, разделив общую часть и платформенную специфику.

Спектр переиспользования кода

Чем больше общего кода, тем выше экономия, но ниже контроль над платформой.

Раздельные коды - нативные приложения, общий только бэкенд
Общая логика - KMP / C++ / Rust ядро, UI нативный
Общая логика + UI через мост - React Native, .NET MAUI
Собственный движок UI - Flutter, NativeScript
Полностью общий код (веб) - PWA, Cordova/Ionic

Преимущества и недостатки

Преимущества

  • Одна кодовая база и одна команда
  • Быстрее выход на рынок (time to market)
  • Ниже стоимость разработки и поддержки
  • Единый дизайн и логика на всех платформах
  • Общие инструменты, тесты, CI/CD

Недостатки

  • Производительность ниже на тяжелых задачах
  • "Почти нативный" вид, не всегда 1:1 с платформой
  • Задержка поддержки новых API ОС
  • Больше размер приложения
  • Зависимость от вендора фреймворка

Экономика выбора

ФакторНативная разработкаКроссплатформенная
Размер команды (MVP, 2 платформы)3-6 инженеров1-3 инженера
Сроки первой версиибазовыев 1,5-2 раза быстрее
Стоимость поддержкирастет линейно от числа платформрастет слабее
Найм специалистов2 профиля (Kotlin + Swift)1 профиль (JS, Dart, C#)
Потолок производительностимаксимальныйобычно достаточный

Для большинства бизнес-приложений экономия перевешивает потери производительности; для игр и AR/VR - нет.

Критерии выбора технологии

Продуктовые

  • Сложность UI и анимаций
  • Нужны ли магазины приложений
  • Целевые платформы (только mobile? desktop? web?)

Командные

  • Есть ли опыт в JS, Dart, C#?
  • Размер и бюджет команды
  • Готовность поддерживать нативный код

Технические

  • Требуемые нативные API и железо
  • Требования к размеру и старту
  • Долгосрочная поддержка

Организационные

  • Риск зависимости от вендора
  • Сообщество и зрелость
  • Наличие экспертизы в компании

Как это работает

Три способа нарисовать пиксели

Нативные представления (views). Фреймворк создает реальные Android View / iOS UIKit-элементы. Так работают React Native, NativeScript, .NET MAUI. Нативный вид и поведение, но каждый вызов идет через границу языков.
Собственный рендеринг. Фреймворк сам рисует на Canvas, не используя нативные элементы. Так работает Flutter (Impeller). Полный контроль над внешним видом и одинаковый результат везде, но нет "нативности" из коробки.
Веб-рендеринг. UI строится браузерным движком (WebView) из HTML/CSS. Так работают Cordova, Ionic, PWA. Максимальная простота, но производительность и доступ к API ограничены.

Мост (Bridge) и JSI

Язык разработки (JS, Dart) и платформа (Kotlin/Swift) - разные миры. Их связывает прослойка.

Классический Bridge

  • Асинхронные сообщения через очередь
  • Данные сериализуются в JSON
  • Задержка на каждом вызове
  • Просто, но медленно при большом потоке

Современный JSI / FFI

  • Прямой синхронный доступ к нативным объектам
  • Без сериализации
  • Минимальные задержки
  • Основа новой архитектуры RN и Dart FFI

FFI (Foreign Function Interface) - общий принцип вызова "чужого" кода без прослойки-обертки.

JIT и AOT компиляция

JIT (Just-in-Time)

  • Компиляция во время работы
  • Быстрый цикл разработки, hot reload
  • Медленнее старт и пиковая производительность
  • Пример: Dart в debug, JS-движки

AOT (Ahead-of-Time)

  • Компиляция заранее, при сборке
  • Быстрый старт и высокая производительность
  • Дольше сборка, hot reload недоступен
  • Пример: Dart в release, .NET AOT, Kotlin/Native

На iOS сторонний JIT запрещен - код должен быть скомпилирован заранее (AOT), что объясняет различия поведения фреймворков.

Горячая перезагрузка (Hot Reload)

  • Изменения в коде видны на устройстве без потери состояния
  • Ускоряет разработку UI в разы: цикл "правка → результат" занимает секунды
  • Есть в Flutter (sub-second), React Native (Fast Refresh), .NET MAUI
  • Важнейший практический фактор выбора - спросите не "быстро ли", а "насколько быстро итерация"

PWA: веб как приложение

PWA: три столпа

  • Устанавливаемость - иконка на главном экране через Web App Manifest, без магазина
  • Надежность - Service Worker кэширует ресурсы и обеспечивает работу offline
  • Возможности - push-уведомления, фоновая синхронизация, доступ к части нативных API

PWA - обычный сайт, который умеет вести себя как приложение. Основное отличие от гибрида: запускается в браузере, а не в оболочке.

PWA: Service Worker

  • Фоновый скрипт-посредник между страницей и сетью
  • Перехватывает все сетевые запросы (fetch) и решает: отдать из кэша или из сети
  • Не имеет доступа к DOM, работает в отдельном потоке
  • Живет независимо от страницы - продолжает работать при закрытой вкладке
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(r => r || fetch(event.request))
    );
});
					

Вместо ручного кэша на практике используют библиотеку Workbox: она дает готовые стратегии и интеграцию со сборщиками.

PWA: стратегии кэширования

СтратегияЛогикаДля чего
Cache Firstсначала кэш, при промахе - сетьстатика, шрифты, иконки
Network Firstсначала сеть, при ошибке - кэшAPI, динамический контент
Stale While Revalidateотдать кэш и обновить в фонечасто меняющиеся данные
Only Cache / Only Networkжесткий выбор источникаoffline-страницы, аналитика

Выбор стратегии под каждый тип ресурса - ключ к быстрой и предсказуемой PWA.

PWA: Web App Manifest

JSON-файл, описывающий приложение для операционной системы.

ПолеНазначение
name / short_nameполное и краткое имя на иконке
start_url / scopeточка входа и область "приложения"
displaystandalone - без адресной строки браузера
theme_color / background_colorцвет статус-бара и заставки
iconsнабор иконок, включая maskable
share_targetприем данных из меню "Поделиться" ОС

PWA: современные возможности

Push и Badge

Уведомления и значок с числом на иконке (iOS - поддержка с 16.4)

Background Sync

Отправка данных при восстановлении связи, даже после закрытия вкладки

File System Access

Работа с локальными файлами, ограниченно на iOS

WebAssembly

Высокопроизводительный код (C/C++/Rust) прямо в браузере

PWA: преимущества и ограничения

Преимущества

  • Не нужен магазин приложений
  • Обновляется автоматически
  • Минимальный размер
  • Индексируется поисковиками
  • Единый код для всех платформ

Ограничения

  • Ограниченный доступ к API ОС
  • Разная поддержка на iOS и Android
  • Нет в App Store / Google Play
  • Заметно на тяжелой графике и анимациях
  • Зависимость от браузера пользователя

PWA: где применять

  • Каталоги, лендинги, медиа-сайты, интернет-магазины
  • Внутренние инструменты (без распространения через магазины)
  • Быстрый MVP и проверка спроса до вложений в нативные приложения
  • Аудитории в регионах с медленным интернетом (offline-first критичен)

PWA - самый дешевый способ "быть на телефоне пользователя", но не полноценная замена магазинному приложению.

Гибридные решения

WebView: что это

  • Встроенный браузерный компонент внутри нативного приложения
  • Показывает HTML/CSS/JS, но живет в нативном контейнере
  • Платформа дает компонент, который обычно в разы мощнее системного браузера по контролю
  • Применяется в гибридных приложениях и для показа веб-контента (платежные формы, справка, виджеты)
Android: WebView          (системный, на Chromium)
iOS:     WKWebView        (на WebKit)
Идея:    HTML/CSS/JS  +  прослойка к нативным плагинам
					

Cordova: история и устройство

  • Появился в 2009 как PhoneGap, позже передан Apache
  • Ядро - приложение на HTML/CSS/JS внутри WebView
  • Плагины дают доступ к камере, геолокации, файлам, контактам
  • Платформы: Android, iOS, Windows
  • Сейчас считается устаревающим, уступил Capacitor

Пользовательский интерфейс - это веб-страница, упакованная в APK/IPA вместе с движком.

Ionic и Capacitor

  • Ionic - библиотека готовых UI-компонентов под Android/iOS, работает с Angular, React или Vue
  • Capacitor - современная замена Cordova от той же команды
  • Генерирует полноценные нативные проекты, к которым есть прямой доступ
  • Поддерживает и веб-платформу (PWA) из той же кодовой базы

Cordova vs Capacitor

ОсобенностьCordovaCapacitor
РазработчикApacheIonic
Нативный проектскрыт в оберткеполноценный, доступен для правок
Плагиныcordova-plugin-*@capacitor/*, можно и cordova-*
Live Reloadограниченполная поддержка
Веб/PWAнетда, из той же базы
Судьбаподдержка снижаетсяактивно развивается

Гибридный подход: итоги

  • Плюсы: крайне низкий порог входа для веб-разработчиков, один код на mobile и web
  • Минусы: производительность тяжелых экранов, сложность нативных плагинов, "веб-ощущение" интерфейса
  • Хорошо подходит: формы, каталоги, корпоративные приложения, контентные сервисы
  • Плохо подходит: игры, сложная анимация, работа с графикой и железом

Нативный рендеринг

.NET MAUI (наследник Xamarin)

  • Язык C#, платформа .NET
  • Общая бизнес-логика и общий UI; при необходимости - прямой доступ к нативным API
  • Проекты: Android, iOS, macOS, Windows из одной кодовой базы
  • Исполняется на Mono/NativeAOT, вызывает нативный код через привязки
  • Логичный выбор, если компания уже на .NET / C#

Xamarin.Forms vs .NET MAUI

ОсобенностьXamarin.Forms.NET MAUI
Платформа .NETстарые версии.NET 8 / 9+
Структура проектаотдельные проекты на платформуединый проект
DesktopнетWindows, macOS
Производительностьсредняявыше, NativeAOT
Статуспрекращена (2024)актуальная платформа

NativeScript

  • Язык: JavaScript / TypeScript, фреймворки Angular, Vue, Svelte
  • Прямой доступ к нативным API без отдельной прослойки
  • Нет WebView: интерфейс строится из настоящих нативных компонентов
  • Ниша: небольшие команды, уже знающие JS/TS и желающие "нативности"
  • Меньшее сообщество и экосистема по сравнению с React Native

React Native: обзор

  • Язык: JavaScript / TypeScript, UI описывается декларативно (React/JSX)
  • Строит интерфейс из реальных нативных компонентов
  • Огромная экосистема npm и рынок труда
  • Самый популярный кроссплатформенный стек для бизнес-приложений
const App = () => (
    <View style={styles.box}>
        <Text>Привет</Text>
        <Button title="Нажми" onPress={handlePress} />
    </View>
);
					

React Native: архитектура

  • Классическая: JS-поток общается с нативным через асинхронный Bridge (сериализация сообщений - узкое место)
  • Новая архитектура (по умолчанию с 0.76): избавляется от моста

JSI

Прямой синхронный вызов нативного кода из JS без сериализации

Fabric

Новый рендерер: синхронное дерево UI и конкурентные обновления

TurboModules

Ленивая загрузка нативных модулей и типобезопасная генерация кода

Итог: быстрее старт, плавнее анимации, меньше накладных расходов.

React Native: Expo

Managed (по умолчанию)

  • Expo управляет нативной частью
  • Быстрый старт, минимум конфигурации
  • Богатый набор встроенных модулей
  • OTA-обновления через EAS Update

Bare / prebuild

  • Полный доступ к нативному коду
  • Любые нативные SDK и библиотеки
  • Нужны Xcode и Android Studio
  • Сложнее сборка и обновления

Expo стал рекомендуемым способом начать проект на React Native.

React Native: навигация и состояние

Навигация (React Navigation)

  • Stack - экран поверх экрана
  • Tab - нижние вкладки
  • Drawer - боковое меню
  • Deep Linking - переходы по ссылкам

Состояние

  • Встроенные хуки - для локального
  • Zustand - минималистичный
  • Redux Toolkit - крупные приложения
  • Jotai/Recoil - атомарный подход

Отдельного "стандарта" нет: выбирайте по размеру приложения и опыту команды.

Flutter и другие экосистемы

Flutter: краткий обзор

  • Создан Google, язык Dart
  • Собственный движок рендеринга (Impeller вместо Skia) - не использует нативные элементы и WebView
  • Одинаковый пиксель-в-пиксель вид на Android, iOS, Web, Windows, macOS, Linux
  • Быстрый hot reload, богатый набор виджетов (Material и Cupertino)
  • Детально - в лекциях 5-7.

Kotlin Multiplatform (KMP)

  • Общая бизнес-логика на Kotlin для Android, iOS, desktop, web, сервера
  • UI остается нативным: Jetpack Compose, SwiftUI, либо Compose Multiplatform
  • Не заменяет Flutter/RN, а дополняет нативную разработку
  • Логичный выбор, если команда уже пишет на Kotlin

Compose Multiplatform

  • Декларативный UI на Kotlin от JetBrains, расширение Jetpack Compose
  • Общий UI-код для Android, iOS, Desktop и Web (Wasm)
  • Хорошо сочетается с KMP: общая и логика, и интерфейс
  • Стабильность для iOS выросла к 2025 году, но экосистема меньше, чем у Flutter

Другие экосистемы

HarmonyOS (Huawei)

  • Язык ArkTS, UI ArkUI
  • Экосистема без Google-сервисов
  • Магазин AppGallery
  • Быстрый рост в Китае

Игры и 3D

  • Unity (C#) - де-факто стандарт
  • Godot - открытый движок
  • Один код на mobile, desktop, консоли
  • Отдельная категория со своими правилами

Инженерные аспекты

Производительность: где узкие места

  • Старт приложения - размер бандла, инициализация движка
  • Плавность анимаций - мост и лишние перерисовки на кадр
  • Списки - виртуализация длинных списков (большие каталоги)
  • Изображения и память - размеры, кэш, утечки
  • Тяжелые вычисления - выносить в нативный код через FFI или изоляты

Производительность - это чаще про архитектуру и профилирование, чем про выбор фреймворка.

Доступ к нативным API

Готовые плагины

Камера, геолокация, уведомления, файлы - уже написаны сообществом

Нативные модули

Своя обертка на Kotlin/Swift вокруг SDK, доступная из основного языка

FFI / привязки

Прямой вызов C/C++/Rust библиотек с минимальными накладными расходами

Если нужен экзотический или свежий API - почти всегда придется писать нативный код самостоятельно.

Тестирование

  • Юнит-тесты общей логики - самый большой выигрыш кроссплатформенности
  • Тесты UI (widget/component) на уровне фреймворка
  • Интеграционные и E2E на реальных устройствах (Appium, Maestro, Detox)
  • Golden-тесты (скриншоты) для контроля визуальных регрессий
  • Матрица устройств: эмуляторы + облачные фермы реальных устройств

CI/CD и публикация

  • Единый конвейер: сборка → тесты → подпись → публикация
  • Облачные сборки (EAS, Codemagic, GitHub Actions) снимают боль с macOS-раннеров
  • Автоматизация версионирования и changelog
  • Ревью в App Store строже, чем в Google Play; сроки публикации разные
  • EU Digital Markets Act (2024) разрешила стороне установку на iOS - но только в ЕС

Обновления: OTA и магазины

Через магазин

  • Полный контроль, но ревью и задержка
  • Пользователь обновляет сам (или авто)
  • Обязателен для смены нативного кода

OTA (over-the-air)

  • Обновление логики и UI без магазина
  • Мгновенный откат (rollback)
  • Нельзя менять нативный код и нарушать правила сторов

Безопасность и данные

  • Хранить секреты в keystore/secure storage, а не в коде и не в открытом виде
  • Проверять SSL-пиннинг для критичных API
  • Осторожно с плагинами третьих сторон: они получают доступ к нативному слою
  • Минимизировать разрешения и прозрачно объяснять их пользователю
  • Учитывать GDPR/152-ФЗ при хранении персональных данных

Доступность и локализация

  • Поддержка экранных читалок (TalkBack, VoiceOver), размеров шрифта
  • Контрастность, крупные зоны нажатия, семантические метки
  • Локализация: строки в ресурсах, учет RTL-языков, форматы дат и чисел
  • Кроссплатформенные фреймворки дают основу, но часть работы - платформая

Сравнение и выбор

Сводная таблица

Критерий PWA Ionic / Capacitor React Native Flutter .NET MAUI KMP
ЯзыкJS/TSJS/TSJS/TSDartC#Kotlin
РендерингWebViewWebViewнативные viewsсвой движокнативные viewsнативный UI
Производительностьсредняясредняяхорошаявысокаяхорошаянативная
Нативный виднетнетпочтикастомныйпочтида
Размер приложенияминимумсреднийсреднийсредний+средний+средний
Магазины приложенийнетдадададада
Desktop / Webwebwebweb + desktopвсевсевсе
Порог входанизкийнизкийнизкий (если React)средний (Dart)средний (C#)средний (Kotlin)
Сообществобольшоесреднееочень большоеочень большоесреднеерастущее

Скорость и стоимость

ТехнологияTime to marketКомандаИтерация
Нативная (2 платформы)самая долгая2+ командыбыстрая
Flutterбыстрая1 командаsub-second hot reload
React Nativeбыстрая1 командаFast Refresh
Ionic / Capacitorсамая быстрая1 командаhot reload / live reload
.NET MAUIсредняя1 командаhot reload
PWAмгновенное развертывание1 командаавтоматически

Кроссплатформенные решения ускоряют выход на рынок примерно в 1,5-2 раза при прочих равных.

Размер приложения (ориентиры)

Технология"Hello World"Типичное приложениеПримечание
Нативная~1 МБ5-20 МБминимум
Flutter~7 МБ15-30 МБдвижок внутри
React Native~7 МБ10-25 МБJS-движок и бандл
Ionic / Capacitor~5 МБ10-20 МБвеб-контент
PWA~100 КБ1-5 МБбез установки

Чем больше "своего" рантайма везет приложение, тем крупнее бандл; размер можно уменьшать (split, tree shaking, AOT).

Дерево решений

Нужны магазины приложений и максимальная нативность?
  └─ да ──> Нужны только Android и iOS?
              ├─ да  ──> Есть опыт React/JS?  -> React Native
              │          Есть опыт C#/.NET?    -> .NET MAUI
              │          Есть опыт Kotlin?     -> KMP + Compose MP
              │          Иначе                 -> Flutter
              └─ нет ──> Нужны desktop/web тоже?
                          └─ да ──> Flutter / KMP / Compose MP
Нужен быстрый MVP, магазин не нужен?
  └─ да ──> PWA (или Ionic/Capacitor, если нужен магазин)
Сложная графика, игры, AR/VR?
  └─ да ──> Нативная разработка / игровой движок
					

Матрица "когда что выбирать"

Нативная: игры, AR/VR, сложная графика, максимум производительности, экзотическое железо
Flutter: стартапы, кастомный дизайн, единый вид, выход на desktop/web
React Native: команда знает React, крупная экосистема, "почти нативный" вид
PWA: простые приложения, каталоги, минимальный бюджет, магазин не нужен
Ionic / Capacitor: есть веб-приложение, нужно быстро "завернуть" в mobile
.NET MAUI / KMP: команда уже в C# или Kotlin, нужна долгосрочная native-first стратегия

Тренды 2026

  • Flutter: рост в desktop/web, зрелый Impeller, но вопрос о стратегии Google
  • React Native: новая архитектура стала стандартом, Expo - рекомендуемый путь
  • KMP и Compose Multiplatform: общая логика и общий UI все чаще в enterprise
  • PWA и веб-возможности: новые API, WebAssembly, push на iOS
  • Регуляторика: DMA в ЕС меняет правила сторонней установки на iOS
  • AI-ассистенты: генерация UI и кода, автоперевод и тесты - ускоряют разработку во всех стеках

Ключевые выводы

  • Кроссплатформенность - это спектр: от общей логики до общего UI
  • Нет универсально лучшей технологии - есть подходящая под задачу и команду
  • Выбор определяют: требуемые API, производительность, опыт команды, сроки, бюджет
  • Основные игроки сегодня: Flutter, React Native, .NET MAUI, KMP, PWA/гибриды
  • Начинайте с MVP (часто PWA), затем масштабируйте до нативного рендеринга при необходимости
  • Помните о цене поддержки: один код ≠ бесплатная кроссплатформенность