На 14 сентября 2026 года Apple подтверждает: требование UIScene определяется SDK, которым собрано приложение, а не только системой на устройстве. Поэтому если вы собираетесь использовать iOS 27 SDK, миграцию UIScene в iOS 27 нужно начинать сразу. Старый SDK оставьте только как временный откат. До переключения производственного Mac проверьте на отдельной машине запуск, deep link, push, смену состояний сцены и Archive. Граница требования описана в обсуждении Apple Developer Forums о влиянии SDK.
Этот материал предназначен для трёх групп:
- для владельцев старых UIKit-проектов, где
UIWindowсоздаётся в AppDelegate и нет Scene Manifest; - для независимых разработчиков с Storyboard, программным интерфейсом или смешанной архитектурой;
- для небольших команд, у которых только один производственный Mac и которым нужна отдельная среда для проверки Xcode 27.
01 Сначала определите, затрагивает ли проект новое требование
Не начинайте с бездумного добавления SceneDelegate. Сначала проверьте, каким SDK создаётся финальный Archive. Именно этот параметр определяет ветку работ.
Откройте итоговый Info.plist из собранного приложения и проверьте:
- присутствует ли
UIApplicationSceneManifest; - есть ли внутри конфигурация для нужной роли сцены;
- указан ли главный Storyboard, если интерфейс запускается из Storyboard;
- возвращает ли
application(_:configurationForConnecting:options:)действительную конфигурацию; - не создаётся ли второе окно параллельно старой логикой AppDelegate.
Apple описывает переход от процессной модели к сценам в официальной документации по UIKit scene-based life cycle. В TN3187 отдельно разобраны типовые варианты миграции старых проектов.
Решение по условиям
Используйте следующий порядок выбора, прежде чем удалять старые callbacks или менять производственный Xcode:
- Если финальная сборка уже выполняется с iOS 27 SDK и у приложения нет рабочей сцены, выбирайте немедленную миграцию. Старый SDK оставьте только для аварийного отката.
- Если релизная ветка ещё собирается старым SDK, а новая ветка проверяется в Xcode 27, выбирайте короткий двойной режим. Зафиксируйте обе версии, каталоги артефактов и условие возврата.
- Если проект уже содержит корректный Scene Manifest и SceneDelegate, не переписывайте архитектуру целиком. Проверьте окно, входящие события, восстановление состояния и повторное подключение сцены.
- Если проблема возникла только после смены SDK, сравните два итоговых
Info.plistи логи запуска. Не делайте вывод, что причиной обязательно является Storyboard или iOS Simulator. - Если мигрированная сборка запускается, но теряет deep link, push или состояние окна, не переключайте production toolchain. Сначала исправьте маршрутизацию событий.
- Если Xcode 27 не может завершить Archive или требует ручного вмешательства после перезапуска Mac, вернитесь к проверенной цепочке и оставьте новую среду изолированной.
Это и есть рабочее решение между немедленной миграцией, коротким двойным режимом и временным откатом. Оно не превращает старый SDK в постоянную стратегию.
Xcode 27 RC был открыт 9 сентября 2026 года, а Apple публикует изменения и ограничения в записях выпуска Xcode и заметках к Xcode 27. Конкретную дату, после которой старый SDK перестанет приниматься для отправки, нельзя выдумывать: на момент проверки Apple её не объявила.
02 Storyboard-проект: перенесите вход сцены, а не только файл делегата
В старом Storyboard-проекте AppDelegate часто выполняет сразу несколько задач:
- настраивает сервисы;
- создаёт
UIWindow; - назначает корневой контроллер;
- разбирает URL;
- запускает авторизацию;
- реагирует на переход приложения в фон.
После перехода на UIScene эти обязанности нужно разделить. AppDelegate отвечает за процесс приложения: конфигурацию SDK, общие сервисы и события, не привязанные к конкретному окну. SceneDelegate отвечает за жизненный цикл визуальной сцены: подключение, активацию, деактивацию и восстановление интерфейса.
Первая проверка: конфигурация сцены
В UIApplicationSceneManifest должна находиться конфигурация для нужной роли, обычно UIWindowSceneSessionRoleApplication. Если Storyboard подключён через настройки сцены, не создавайте такое же окно вручную в scene(_:willConnectTo:options:). Иначе система может получить два независимых пути инициализации.
Проверьте следующие результаты:
- холодный запуск открывает ожидаемый экран;
- после перехода в фон и возврата не создаётся новый стек навигации;
- состояние формы или выбранного объекта восстанавливается по принятой логике;
- повторное подключение сцены не вызывает повторную регистрацию сервисов;
- приложение не показывает пустое окно, потому что корневой контроллер назначен дважды или не назначен вообще.
Документация Apple о конфигурации поддерживаемых сцен полезна именно для проверки связки Manifest, роли и конфигурации. Не ограничивайтесь тем, что Xcode перестал показывать ошибку компилятора.
Граница между AppDelegate и SceneDelegate
Оставьте в AppDelegate:
- создание контейнера зависимостей;
- настройку общих логгеров и хранилищ;
- регистрацию сервисов, которым не нужен конкретный экран;
- обработку событий, относящихся ко всему процессу.
Перенесите в SceneDelegate:
- получение
UIWindowScene; - подготовку окна;
- установку корневого контроллера, если она не выполняется Storyboard;
- восстановление состояния конкретной сцены;
- обработку событий, связанных с URL или пользовательской активностью сцены.
Не удаляйте старые методы сразу из производственной ветки. Сначала сохраните коммит до миграции. Если новая сборка не проходит запуск, это позволит вернуться к проверенной цепочке без ручного восстановления кода.
03 Программный UIKit: восстановите полную цепочку UIWindow
В проекте без Storyboard типичная ошибка выглядит так: разработчик добавляет SceneDelegate, но оставляет создание окна в AppDelegate. В результате приложение компилируется, однако получает пустой экран, неправильный контроллер или ссылку на окно, которое больше не связано с текущей сценой.
Для программного интерфейса в scene(_:willConnectTo:options:) должна быть логическая цепочка:
- получить
UIWindowSceneиз параметраscene; - создать
UIWindowс этимUIWindowScene; - собрать корневой контроллер;
- присвоить его свойству
rootViewController; - сохранить окно в SceneDelegate;
- вызвать
makeKeyAndVisible()после завершения настройки.
Apple описывает API UIScene как объект, представляющий отдельный жизненный цикл пользовательского интерфейса. Это важное отличие от старого предположения, что у процесса всегда есть одно глобальное окно.
Проверьте исходный код поиском по таким шаблонам:
AppDelegate.window;UIApplication.shared.keyWindow;- глобальная переменная
window; - получение первого окна через
windows.first; - переход к экрану через единственный экземпляр навигационного контроллера;
- сохранение ссылки на контроллер, который принадлежит уже закрытой сцене.
Эти конструкции могут работать в простом односценовом проекте, но становятся источником ошибок при восстановлении, iPad-многозадачности и Mac Catalyst. Переделайте код так, чтобы текущая сцена передавалась явно. Экран должен получать контекст своей сцены, а не угадывать его через глобальное состояние.
Вторая проверка: отсутствие повторной инициализации
Создание окна и регистрация сервисов — разные операции. Если контейнер зависимостей создаётся при каждом подключении сцены, вы можете получить повторные подписки, дублирующиеся уведомления и несколько экземпляров аналитического SDK. Если же окно создаётся только в AppDelegate, новая сцена может не получить интерфейс.
Отдельно протестируйте:
- первый запуск после установки;
- повторную активацию приложения;
- закрытие и повторное подключение сцены;
- восстановление после выгрузки процесса;
- открытие ссылки, которая должна привести к конкретному экрану.
04 Deep link, push и авторизация: разделите события процесса и сцены
Миграция UIScene ломается не только на окне. В старых проектах все входящие события часто проходят через AppDelegate. После перехода часть событий приходит вместе с подключением сцены, а часть — уже активной сцене.
Проверьте четыре источника:
- URL Scheme;
- Universal Link;
- ответ на push-уведомление;
- пользовательское действие или
NSUserActivity.
При подключении используйте данные connectionOptions. Apple перечисляет доступные варианты в документации ConnectionOptions. Это особенно важно для cold start: процесс может быть запущен именно потому, что пользователь открыл ссылку или нажал уведомление.
Для уже работающей сцены обработчик должен учитывать текущее состояние интерфейса. Нельзя просто положить URL в глобальную переменную и немедленно открыть экран, если навигационный стек ещё не создан.
Проведите три независимых сценария:
- приложение полностью завершено, затем открыта ссылка;
- приложение находится в фоне, затем принято уведомление;
- приложение уже активно, затем получен новый URL или пользовательское действие.
Используйте только обезличенные схемы, тестовые payload и фиктивные идентификаторы. Не сохраняйте в логах реальные URL, токены, идентификаторы команды, Bundle ID или данные пользователей.
Особое внимание уделите сторонним SDK. SDK авторизации может ожидать вызов в AppDelegate. SDK push может считать, что приложение всегда имеет одно окно. Аналитика может регистрировать переход до готовности сцены. Для каждого такого компонента зафиксируйте:
- какой метод вызывается;
- в каком состоянии находится сцена;
- кто владеет переходом;
- что происходит при повторной доставке события;
- куда попадает событие, если сцена ещё не подключена.
05 iPad, несколько окон и Mac Catalyst: мигрируйте границы состояния
Использование UIScene не означает, что вы обязаны немедленно превращать приложение в полноценный многоконный продукт. Но оно означает, что интерфейс нельзя бездумно связывать с единственным глобальным окном.
Разделите состояние на три уровня:
- процессное — настройки сервисов, база данных, общие зависимости;
- сценовое — открытый документ, выбранный аккаунт, навигационный контекст;
- экранное — временное состояние конкретного контроллера.
Для iPad проверьте восстановление после перехода между окнами и режимами многозадачности. Для документного приложения убедитесь, что два окна не используют один изменяемый объект без синхронизации. Для Mac Catalyst отдельно проверьте закрытие окна, повторное открытие и освобождение ресурсов.
Логику внешнего дисплея не переносите механически. Сначала сопоставьте используемые роли сцен с актуальной документацией Apple. Если проект поддерживает только один экран, не расширяйте миграцию до полной архитектурной перестройки без теста, который доказывает необходимость такого изменения.
06 Чек-лист двойной проверки в Xcode 27
Выполняйте новую сборку на отдельном Mac, а не сразу на единственной машине, которая обслуживает релиз. Для изоляции используйте отдельные каталоги проекта, DerivedData и Archive.
Третья проверка: зафиксируйте две инструментальные цепочки
Перед изменением среды сохраните:
- текущую версию Xcode;
- выбранный SDK;
- путь к инструменту сборки;
- параметры подписи;
- схему и конфигурацию Archive;
- расположение сертификатов и профилей;
- скрипты CI или локального запуска;
- последний успешный идентификатор Archive.
Затем создайте отдельную ветку миграции. Не меняйте системный Xcode по умолчанию, пока новая ветка не прошла установку и запуск.
Четвёртая проверка: соберите старый вариант
Для старого варианта сохраните:
- результат Build;
- установочную сборку;
- лог холодного запуска;
- результат проверки deep link;
- результат push-сценария;
- Archive и его метаданные.
Здесь цель не в том, чтобы доказать пригодность старого SDK для будущего релиза. Нужно получить контрольную точку, с которой можно сравнить поведение после миграции.
Пятая проверка: соберите вариант с Xcode 27
В изолированной среде выполните Build, Test и Archive. После установки проверьте:
- холодный запуск;
- запуск после завершения процесса;
- уход в фон и возврат;
- восстановление сцены;
- URL Scheme;
- Universal Link;
- push при завершённом приложении;
- push в фоне;
- push при активном интерфейсе;
- повторное подключение окна;
- Archive с релизной конфигурацией.
Сохраняйте не только сообщение об ошибке, но и команду, схему, SDK, время запуска, состояние приложения и обезличенный лог. Это сокращает повторную диагностику, если проблема проявится только на производственной машине.
Шестая проверка: испытайте удалённое выполнение
Если сборка выполняется на удалённом Mac, проверьте операционные сбои отдельно от проблем UIScene:
- разрыв VNC-сеанса;
- повторное подключение;
- перезапуск хоста;
- продолжение или повтор задания после обрыва;
- наличие Archive после завершения без открытого графического сеанса;
- доступность сертификатов и provisioning profile;
- совпадение версии Xcode после перезагрузки.
Удалённый Mac удобен не потому, что заменяет тестирование, а потому, что позволяет не рисковать единственной производственной средой. Для такой схемы можно отдельно изучить варианты удалённого доступа CALMVPS и заранее разделить тестовую и релизную рабочие каталоги.
07 Итоговый контроль перед переключением производственного Mac
Перед изменением Xcode по умолчанию выполните этот чек-лист:
- [ ] Финальная сборка создана с явно выбранным SDK.
- [ ] В
Info.plistприсутствует ожидаемыйUIApplicationSceneManifest. - [ ] Для Storyboard не создаётся второе окно вручную.
- [ ] Для программного UIKit
UIWindowсвязан с текущимUIWindowScene. - [ ] В AppDelegate не осталось логики, которая требует конкретного окна.
- [ ] SceneDelegate не создаёт повторно контейнеры и глобальные сервисы.
- [ ] Deep link проверен из завершённого, фонового и активного состояния.
- [ ] Push проверен для cold start, фонового режима и активного интерфейса.
- [ ] Состояние сцены проверено после ухода в фон и повторной активации.
- [ ] Archive сохранён отдельно от старой версии.
- [ ] Старый Xcode и прежняя конфигурация можно восстановить без ручной реконструкции.
- [ ] Удалённый Mac пережил разрыв сеанса и перезапуск хоста.
- [ ] В логах отсутствуют реальные токены, аккаунты, URL, Bundle ID и адреса хостов.
Если любой пункт, связанный с запуском, маршрутизацией или Archive, не выполнен, не переключайте production toolchain. Оставьте новую ветку изолированной и исправьте конкретный сбой.
08 FAQ: частые решения для старого UIKit App
Почему iOS 27 требует UIScene
Причина связана с SDK сборки. Если приложение собрано новым iOS 27 SDK, старый процессный жизненный цикл без рабочей конфигурации сцены больше нельзя считать достаточным. Версия системы на тестовом устройстве не отменяет это условие. Поэтому сравнивайте итоговый Archive и Info.plist, а не только настройки Simulator.
Как перейти с одного AppDelegate на SceneDelegate
Не переносите весь AppDelegate целиком. Сначала добавьте Scene Manifest, затем перенесите окно и корневой контроллер в SceneDelegate. Процессную инициализацию оставьте в AppDelegate. Для Storyboard свяжите главный Storyboard с конфигурацией сцены, а для программного UIKit создайте UIWindow на основе текущего UIWindowScene.
Куда перенести ссылки и push
События, из-за которых создаётся новая сцена, проверяйте через ConnectionOptions. События уже подключённой сцены обрабатывайте в соответствующих методах SceneDelegate или маршрутизатора. После этого проверьте авторизацию, push и аналитику: сторонний компонент может по-прежнему ожидать старый AppDelegate.
Допустим ли временный откат на старый Xcode
Да, как аварийная мера для сохранения производственной публикации, если старый SDK ещё принимается в вашей цепочке. Нет, как замена миграции. У вас должна быть зафиксированная причина отката, сохранённая старая среда и отдельный план возврата к Xcode 27 после исправления запуска.
Как сравнить две сборки удалённо
Собирайте их в разных каталогах, явно выбирайте Xcode и не смешивайте DerivedData. Для каждой версии сохраните Archive, лог установки, запуск, ссылки, push и результат восстановления сцены. Только после этого сравнивайте поведение и решайте, можно ли переключать производственный Mac.
09 Что переключать в продакшне и когда откатываться
Переводите основную машину на новую цепочку только после того, как мигрированная сборка:
- устанавливается без ручных исправлений;
- запускается после чистой установки;
- открывает тестовую ссылку из завершённого состояния;
- принимает push во всех трёх состояниях;
- корректно восстанавливает интерфейс;
- создаёт Archive в релизной конфигурации;
- проходит повторную сборку после перезапуска удалённого хоста.
Откат нужен, если новая ветка не создаёт окно, теряет deep link, дублирует инициализацию, не завершает Archive или требует ручного вмешательства после перезагрузки. Откат не должен удалять изменения миграции. Вернитесь к сохранённой инструментальной цепочке, соберите доказательство проблемы и продолжайте работу в изолированной ветке.
Если единственный Mac сейчас одновременно принимает релизные задания и должен перейти на Xcode 27, покупка отдельного оборудования не всегда оправдана для короткого этапа проверки. В такой ситуации изолированный удалённый Mac позволяет вынести риск из производственного контура: вы копируете обезличенный проект, проверяете запуск и Archive, а затем принимаете решение о переключении. Сравнить доступные варианты можно на странице аренды Mac от CALMVPS.
Локальный Mac лучше для постоянной тяжёлой нагрузки, физического тестирования периферии и команд, которым нужен стабильный собственный хост. Удалённая аренда менее удобна при нестабильном соединении, длительных интерактивных сессиях и требованиях к физическому устройству. Но для временной миграции, двойной проверки Xcode 27 и безопасного отката она обычно практичнее, чем менять единственную машину прямо перед релизом.