Урок по треку React Native 7 мин

Сборка, подпись и OTA-обновления

Сборки и подпись, EAS Build и Submit, OTA через expo-updates, runtimeVersion и правила сторов.

У React Native есть преимущество, которого нет у полностью нативных приложений: часть изменений можно доставить пользователям без ревью магазина, обновив JS-бандл «по воздуху» (OTA). На собесе спрашивают, как это работает, где границы и как устроен обычный релиз.

Из чего состоит приложение

  • Нативная часть: скомпилированный код React Native, нативных модулей, Hermes. Меняется только новой сборкой через магазин.
  • JS-бандл и ассеты: ваш код (в Hermes скомпилирован в байткод), картинки, шрифты. Именно его и подменяет OTA.

Обычный релиз

Android: ./gradlew bundleRelease собирает AAB, подписанный ключом загрузки; Google Play с Play App Signing переподписывает приложение своим ключом. versionCode должен расти с каждой загрузкой.

iOS: архив в Xcode с сертификатом дистрибуции и provisioning profile, загрузка в App Store Connect, TestFlight, ревью. CFBundleVersion (номер сборки) растёт с каждой загрузкой.

EAS Build собирает в облаке Expo, сам управляет ключами и сертификатами (или берёт ваши), а EAS Submit отправляет сборку в магазины. Профили в eas.json: development, preview, production. Номер сборки удобно доверить EAS (autoIncrement), чтобы он не зависел от того, кто собирал.

OTA: как работает expo-updates

  1. При запуске приложение проверяет сервер обновлений (по умолчанию при старте, можно вручную).
  2. Если есть обновление, совместимое с этой сборкой, оно скачивается.
  3. Применяется при следующем запуске (или сразу через reloadAsync, если вы решили перезагрузить приложение).
  4. Если новый бандл падает при запуске, expo-updates откатывается на предыдущий рабочий.

runtimeVersion определяет совместимость: обновление получат только сборки с тем же runtimeVersion. Если вы добавили нативную библиотеку или обновили React Native, это новая нативная часть, нужен новый runtimeVersion и новая сборка в магазин. Иначе JS обратится к нативному модулю, которого нет, и приложение упадёт. Политика fingerprint вычисляет runtimeVersion по хешу нативной части автоматически.

Каналы и ветки EAS Update: сборка привязана к каналу (production, preview), канал указывает на ветку обновлений. Так можно сначала выкатить исправление на тестовый канал, а потом в продакшен, или раскатить на процент пользователей и откатить.

CodePush

Раньше популярным решением был Microsoft CodePush через App Center. App Center закрыт в марте 2025 года, поэтому новые проекты используют EAS Update или собственный сервер обновлений по открытому протоколу Expo Updates (есть и сторонние self-hosted реализации).

Правила магазинов

Apple и Google разрешают загружать интерпретируемый код (JS), если обновление не меняет основное назначение приложения, не добавляет функций в обход ревью и не обходит защиту и платежи. OTA подходит для исправления багов, текстов и небольших улучшений, но не для «нового приложения внутри старого». Серьёзные изменения проходят через ревью.

Безопасность и качество OTA

  • Подпись обновлений (code signing в expo-updates), чтобы приложение принимало только ваши бандлы.
  • Поэтапный раскат и мониторинг падений (Sentry, Crashlytics) с привязкой к id обновления.
  • Загрузка source maps для каждого обновления, иначе стек-трейсы не разобрать.
  • Не делать обязательной перезагрузку посреди действия пользователя.

Частые ошибки

  • Отправить OTA, которое использует новую нативную библиотеку, на старые сборки.
  • Забыть сменить runtimeVersion после обновления React Native.
  • Считать OTA заменой магазинного релиза для крупных функций.
  • Не загрузить source maps.
  • Хранить ключи подписи на ноутбуке одного разработчика.

Что спросят на собесе

  • Что можно и что нельзя обновить по воздуху.
  • Как runtimeVersion защищает от несовместимых обновлений.
  • Как откатить неудачное OTA-обновление.
  • Что разрешают правила App Store и Google Play.
  • Как устроен процесс релиза и подписи на Android и iOS.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.