Если вы собираете приложение под SDK iOS 27, не перейдя на жизненный цикл UIScene, оно просто не запустится. А приложение, которое не запускается, никогда не регистрируется для push — ваши уведомления замолкают без единого крэш-репорта, указывающего на push как причину. Симптом выглядит как проблема доставки. На деле это проблема запуска.

Ловушка именно в том, что push и старт приложения живут в разных углах кодовой базы — и в разных углах головы разработчика. iOS 27 связывает их воедино. В этом гайде разберём, что именно ломается, точную цепочку, которая глушит ваши уведомления, кого это касается, и чек-лист миграции, чтобы всё починить. Pushwoosh — платформа для работы с клиентами, и наш iOS SDK уже покрывает этот переход, но фикс ниже актуален независимо от того, используете вы наш SDK или нет.

📖

Часть нашего гайда по релизу iOS 27. Пока идёт работа над SDK, стоит заодно проверить реальный охват устройств.

Что на самом деле ломается

На WWDC25 Apple анонсировала, что версия после iOS 26 сделает жизненный цикл UIScene обязательным для любого UIKit-приложения, собранного на последнем SDK. iOS 27 — это та самая версия. Требование уже применяется.

Важен именно триггер, и большинство панических тредов путают его: применение начинается, когда вы собираете приложение под SDK iOS 27 в Xcode 27, а не просто когда пользователь запускает уже существующее приложение на iPhone с iOS 27. Ваша текущая опубликованная сборка продолжает работать на обновлённых телефонах. Слом наступает с вашим следующим релизом, собранным на новом SDK. Это различие даёт вам время — но только до следующей отправки в App Store.

А вот что не меняется, и именно тут алармистские посты обычно перегибают: сам поток APNs остаётся нетронутым. Вы всё так же запрашиваете разрешение, всё так же вызываете registerForRemoteNotifications() и всё так же получаете device token в didRegisterForRemoteNotificationsWithDeviceToken. Этот колбэк остаётся в вашем AppDelegate, и на iOS 27 сейчас нет scene-эквивалента, куда его можно было бы перенести. С механикой push всё в порядке. Apple изменила именно запуск.

Цепочка, которая глушит ваш push

Когда приложение без scene-манифеста собирается под SDK iOS 27, сбой разворачивается в такой последовательности:

  1. UIKit требует наличия scene при запуске и не находит UIApplicationSceneManifest в вашем Info.plist.
  2. Приложение завершается до того, как выполнится хоть один метод AppDelegate.
  3. Поскольку application(_:didFinishLaunchingWithOptions:) никогда не выполняется, registerForRemoteNotifications() тоже не срабатывает.
  4. Без регистрации didRegisterForRemoteNotificationsWithDeviceToken никогда не вызывается, поэтому приложение никогда не получает токен APNs.
  5. Нет токена — нет доставки. А поскольку приложение упало при старте, логи указывают на запуск, а не на пайплайн уведомлений.

Именно поэтому на дебаг уходит столько времени: симптом и причина находятся очень далеко друг от друга. Вы видите пропавшие доставки и начинаете проверять провайдера push, пейлоады, сертификаты. Реальная причина — тремя слоями выше, в файле манифеста, который вообще не имеет отношения к уведомлениям.

Кого это касается

Требование затрагивает UIKit-приложения, но то, насколько сильно, зависит от вашего стека.

СтекНасколько затронут
Нативный UIKitНапрямую. Если вы владеете жизненным циклом AppDelegate, вы владеете и миграцией.
SwiftUIРиск ниже, если используется протокол App и WindowGroup, которые уже построены на scene. Приложениям, всё ещё опирающимся на UIApplicationDelegateAdaptor с кастомной логикой окна, стоит присмотреться внимательнее.
FlutterНа последних версиях фреймворк автоматически мигрирует приложения с неизменённым AppDelegate, но любую кастомную нативную логику придётся переносить вручную.
React NativeЗависит от ваших нативных модулей и любых обёрток сторонних SDK, которые цепляются за AppDelegate.
Стек
1 / 4
Нативный UIKit
Насколько затронут
Напрямую. Если вы владеете жизненным циклом AppDelegate, вы владеете и миграцией.
Стек
2 / 4
SwiftUI
Насколько затронут
Риск ниже, если используется протокол App и WindowGroup, которые уже построены на scene. Приложениям, всё ещё опирающимся на UIApplicationDelegateAdaptor с кастомной логикой окна, стоит присмотреться внимательнее.
Стек
3 / 4
Flutter
Насколько затронут
На последних версиях фреймворк автоматически мигрирует приложения с неизменённым AppDelegate, но любую кастомную нативную логику придётся переносить вручную.
Стек
4 / 4
React Native
Насколько затронут
Зависит от ваших нативных модулей и любых обёрток сторонних SDK, которые цепляются за AppDelegate.

Острый угол для кросс-платформенных команд — не сам фреймворк, а SDK, установленные поверх него. Любой push- или аналитический SDK, устанавливающийся через обёртку вашего AppDelegate, может заблокировать миграцию на scene, пока вендор не выпустит поддержку scene. Если точкой входа @main владеет зависимость, вы ждёте её релиза, а не только своего. Мы видели миграцию, застрявшую на 2 недели именно из-за этого — одна несговорчивая зависимость, при том что всё, что команда контролировала сама, было готово. Проверьте совместимость вашего notification SDK со scene, прежде чем считать миграцию делом одного дня. Если вы на нашем Flutter-плагине, страница известных проблем миграции — первое, что стоит прочитать.

Чек-лист миграции

Сам фикс не менялся с момента появления scene в 2019 году. Изменилось то, что он перестал быть опциональным.

  • Добавьте scene-манифест. Поместите UIApplicationSceneManifest в ваш Info.plist или настройте scene через код методами UIApplicationDelegate и UISceneDelegate.
  • Создайте SceneDelegate. Если его нет — добавьте и убедитесь, что он реально компилируется в ваш target. SceneDelegate, который существует в проекте, но никогда не был добавлен в build sources, — частая ловушка.
  • Перенесите создание окна. Владение окном уходит из AppDelegate. Замените UIWindow(frame:) на UIWindow(windowScene:) и разместите корневой view из scene.
  • Перенесите обработку UI-жизненного цикла. Переходы foreground, background и active/inactive переезжают в UISceneDelegate. После миграции UIKit перестаёт вызывать методы UI-состояния на вашем AppDelegate, так что всё, что там осталось, тихо умирает.
  • Оставьте регистрацию push на месте. Регистрация токена и её колбэки остаются в AppDelegate. Не переносите их в погоне за симметрией.
  • Мигрируйте обработку URL и диплинков. Входящие URL теперь приходят через методы scene. Если ваши push открывают конкретные экраны — именно этот шаг сохраняет работоспособность диплинков.
  • Пересоберите и проверьте выдачу токена. Соберите на SDK iOS 27, запустите и убедитесь, что приложение получает токен APNs. Если нет — гайд по отладке ошибок iOS подскажет, с чего начать. Проверяйте на реальной сборке до релиза, а не после.

Сохраните AppDelegate. Это тот момент, который команды упускают, читая «AppDelegate уходит». Он никуда не уходит. Его роль сужается до событий уровня процесса и приложения, пока UI-жизненный цикл переезжает в scene. Регистрация push — это забота уровня приложения, и именно поэтому она остаётся на месте.

Почему ставка только на push — хрупкая стратегия

Отойдём на шаг назад. Один-единственный ключ в манифесте, в файле, который почти никто в команде не открывает, способен полностью отключить ваш push-канал в следующем релизе. Это очень много хрупкости, завязанной на один канал доставки.

И это накладывается на потолок, который у push есть в принципе: opt-in. Даже при продуманной стратегии запроса разрешения значительная часть пользователей так и не даёт разрешение на push, так что в хороший день push доходит лишь до части вашей базы. Такие сбои платформы только увеличивают разрыв между тем, до кого вы можете достучаться, и тем, до кого достучались реально.

Команды, которые спокойно переживают такие изменения, — это те, кто не зависит от одного канала. Когда push — лишь один из способов доставки наряду с email, SMS и in-app, регрессия на старте одной платформы лишь немного снижает охват, а не обнуляет его. Это и есть тот запас прочности, который нужен именно на такой случай, как этот от Apple.

👉🏻

Пока идёт работа над SDK, самое время заодно пересмотреть, что делает уведомление действительно достойным отправки.

Проверьте вашу настройку push для iOS 27 вместе с Pushwoosh

SDK Pushwoosh уже поддерживает scene-жизненный цикл iOS 27, а мультиканальная настройка означает, что сбой на одной платформе никогда не утащит за собой всю вашу стратегию уведомлений. Если миграция в процессе и что-то не регистрируется, FAQ по iOS SDK закрывает большинство типовых проблем. А если нужен взгляд со стороны на то, переживёт ли ваша регистрация push этот переход, — мы готовы пройтись по вашей интеграции вместе с вами.

Проверьте настройку push для iOS 27
Проверить совместимость с iOS 27

Частые вопросы

Само по себе — нет. Приложение, уже опубликованное в App Store, продолжает работать на устройствах с iOS 27. Слом происходит, когда вы собираете новую версию под SDK iOS 27, не перейдя на scene-жизненный цикл, — такая сборка просто не запустится. Ваша текущая опубликованная сборка в безопасности до следующей отправки.

Pushwoosh Team
Контент-команда в Pushwoosh
Поделиться

Похожие статьи

Показать все