Apple указывает, что приложения, собранные с iOS 27 SDK или более новой версией SDK, должны содержать допустимую конфигурацию экрана запуска; при её отсутствии сервер может вернуть ошибку ITMS-90870. Это подтверждено в технической записке Apple TN3208. Поэтому исправляйте не только настройки проекта: после изменения проверьте Info.plist внутри Release Archive и повторно загрузите сборку. Если production пока собирается на iOS 26 SDK, его можно временно сохранить, но миграцию в ветку Xcode 27 следует начать уже сейчас.
Последнее обновление: 27 августа 2026 года. Сведения сверены с iOS/iPadOS 27 Release Notes, требованиями к Xcode, документацией App Store Connect и TN3208.
Кому нужен этот runbook
Эта инструкция предназначена для разработчиков, которые готовят существующее приложение на SwiftUI, UIKit, Flutter, React Native или другом кроссплатформенном стеке к сборке Xcode 27.
Она особенно важна для проектов с несколькими Target, расширениями, разными схемами и автоматически создаваемыми plist-файлами. Отдельный раздел посвящён сборщикам на удалённом Mac и автоматическим скриптам, где локальное исправление нередко расходится с фактическим бинарным файлом.
Граница применимости: SDK против минимальной версии iOS
Главная проверка выполняется по SDK, с которым собрано приложение, а не по значению Deployment Target. Если проект поддерживает iOS 18 или iOS 26, но его Release Archive создан с iOS 27 SDK, требование к экрану запуска всё равно применяется.
И наоборот: минимальная версия iOS 27 сама по себе не является условием, которое запускает эту проверку. Нельзя исправить проблему простым изменением IPHONEOS_DEPLOYMENT_TARGET.
В официальных материалах Apple зафиксированы следующие границы:
| Ситуация сборки | Что считать результатом | Действие |
|---|---|---|
| Release Archive создан с iOS 27 SDK или выше | Требование к экрану запуска применяется | Проверить конечный Info.plist и ресурсы |
| Production пока создаётся с iOS 26 SDK | Временная совместимость возможна | Не менять релиз в последний момент, но завести ветку миграции |
| Xcode 27 Beta 5 | Загрузка сборки в TestFlight поддерживается | Использовать для проверки цепочки, не приравнивать к production |
| Будущая официальная версия Xcode 27 | Дата выпуска не подтверждена в перечисленных материалах | Проверить актуальные требования перед переключением |
Apple подтвердила поддержку загрузки сборок Xcode 27 Beta 5 в TestFlight. Это не означает, что beta-сборка автоматически разрешена для полноценного выпуска в App Store. Примечания к выпускам App Store Connect и текущие требования к загружаемым сборкам нужно проверять отдельно перед производственным переключением.
Полнота декларации экрана запуска
Для серверной проверки важен итоговый plist приложения. Apple допускает разные способы декларации, но ключ должен быть распознаваемым, непустым и связанным с реальным ресурсом проекта.
Наиболее прямой современный вариант — словарь UILaunchScreen. Его структура и доступные параметры описаны в документации Apple по UILaunchScreen. Для старого проекта допустима ссылка на файл LaunchScreen.storyboard через UILaunchStoryboardName, если storyboard действительно входит в нужный Target и попадает в Archive.
Как выбрать UILaunchScreen или LaunchScreen.storyboard?
Используйте UILaunchScreen, если проект уже обслуживается через современные настройки Target и не требует сложной логики storyboard. Этот способ уменьшает число файлов, которые могут выпасть из нужной конфигурации.
Оставляйте LaunchScreen.storyboard, если экран запуска исторически построен на storyboard, содержит совместимые ограничения и уже используется несколькими вариантами приложения. Не следует механически добавлять оба подхода. Сначала определите, какая декларация является источником истины для конкретного Target, затем проверьте её в готовом .app.
Нельзя добавлять пустой ключ, имя несуществующего storyboard или файл, который исключён из Copy Bundle Resources. Такая правка может выглядеть убедительно в исходниках, но не исправит серверную проверку.
Минимальная проверка должна отвечать на четыре вопроса:
- есть ли в конечном
Info.plistдопустимый ключ; - заполнено ли его значение;
- существует ли указанный storyboard или ресурс;
- относится ли этот ресурс к тому App Target, который загружается.
Руководство Apple по способам задания экрана запуска помогает сопоставить выбранный способ с настройками Target. Но документация проекта и исходный plist не заменяют проверку Archive.
Разные типы проектов и места исправления
SwiftUI с автоматически создаваемым plist
В SwiftUI часть значений может формироваться настройками Target, а не отдельным файлом, который разработчик редактирует вручную. Поэтому отсутствие видимого Info.plist не означает отсутствие итоговой декларации.
Откройте нужный App Target и проверьте раздел, связанный с конфигурацией экрана запуска. Затем найдите в Build Settings параметры, влияющие на генерацию plist. Если скрипт добавляет значения на этапе сборки, зафиксируйте его входы и выходы.
Автоматически созданный файл может отличаться между Debug и Release. Именно поэтому проверка только в Xcode Editor недостаточна.
UIKit с ручным Info.plist
В UIKit-проекте найдите plist, указанный в настройке INFOPLIST_FILE. Не редактируйте случайный файл с похожим именем. В проекте могут существовать отдельные plist для приложения, расширения, тестового Target и различных конфигураций.
Для storyboard-подхода проверьте:
- имя файла без лишних пробелов;
- включение storyboard в нужный Target Membership;
- наличие файла в Copy Bundle Resources;
- соответствие значения
UILaunchStoryboardNameфактическому имени ресурса.
Справочник Build Settings Reference нужен здесь не для копирования всех параметров, а для поиска источника, который перезаписывает конечный plist.
Кроссплатформенная генерация
Flutter, React Native и другие инструменты могут восстанавливать iOS-проект после изменения зависимостей или запуска подготовительного скрипта. Исправление в сгенерированном файле может исчезнуть при следующей генерации.
Сначала меняйте шаблон или нативную часть, из которой создаётся проект. После этого запускайте обычную подготовку зависимостей, открывайте рабочую область, выбирайте производственный Target и только затем создавайте Archive.
Несколько Target и конфигураций
Для каждого распространяемого приложения составьте отдельную строку проверки. Основной App Target, Notification Service Extension и виджет не следует смешивать: сервер принимает основной пакет, а наличие ключа только в расширении проблему не решает.
| Объект проверки | Где искать | Частая ошибка |
|---|---|---|
| Основное приложение | Release Target и его INFOPLIST_FILE |
Исправлен Debug вместо Release |
| Extension | Собственный plist и Target Membership | Настройки расширения принимают за настройки приложения |
| Scheme | Archive action и выбранная Configuration | Локально запускается Debug, загружается Release |
| Скрипт сборки | Фаза Run Script, генератор plist | Значение перезаписывается после редактирования |
Финальный .app |
Payload/*.app/Info.plist |
Проверен исходник, но не бинарный пакет |
Согласованность Release Archive
Исправление считается завершённым только тогда, когда исходная настройка, результат сборки и отправленный пакет согласованы между собой. Это три разных уровня. Ошибка на любом из них возвращает нас к исходной проблеме.
Как проверить Archive, не доверяя интерфейсу Xcode?
Выполните последовательность, используя обезличенные имя проекта и Bundle ID.
- Откройте production Scheme и убедитесь, что Archive action использует Release Configuration.
- Создайте новый Archive обычным способом через Product → Archive. Не используйте старый пакет из Organizer.
- В Organizer экспортируйте копию
.xcarchiveдля проверки и аудита. - Откройте каталог
Products/Applications/ИмяПриложения.appвнутри архива. - Проверьте
Info.plistименно внутри этого.app, а не файл в корне репозитория. - Сопоставьте ключ запуска с ресурсом: для storyboard проверьте файл в пакете, для
UILaunchScreen— итоговые значения словаря. - Просмотрите подпись, Bundle ID и выбранную схему перед загрузкой.
- Сохраните обезличенные сведения о настройках, результат проверки пакета и лог загрузки.
Пример командной проверки после копирования архива в рабочий каталог:
plutil -p "Payload/Приложение.app/Info.plist"
find "Payload/Приложение.app" -iname "*Launch*"
Команды не исправляют пакет. Они только показывают, что реально будет подписано и отправлено. Не включайте в публикацию сертификаты, токены, пути с именами клиентов и полные идентификаторы учётных записей.
Важно: удаление DerivedData или переустановка Xcode не являются стандартным исправлением ITMS-90870. Сначала найдите отсутствующий ключ, неправильный Target или перезаписывающий скрипт. Очистка кэша имеет смысл только после устранения причины, если старая сборка действительно используется повторно.
Проверка первого запуска
Серверная проверка и визуальный результат — разные метрики. Пакет может пройти ITMS-90870, но показать пользователю старую картинку, пустой экран или неправильно расположенный элемент.
Проверяйте первый запуск после удаления старой установки. Apple отдельно рекомендует такой подход, поскольку уже установленное приложение и симулятор могут сохранять старое состояние. Документация Apple по экрану запуска содержит базовые требования к декларации и поведению.
Порядок приёмки:
- удалите приложение с устройства или симулятора;
- установите свежую Release-сборку;
- завершите приложение и запустите его повторно;
- проверьте пустой фон, старое изображение и неожиданное масштабирование;
- проверьте обрезку на разных соотношениях сторон;
- осмотрите безопасную область и положение основных элементов;
- повторите проверку на физическом iPhone или iPad перед публикацией.
Симулятор подходит для быстрой проверки ресурса и компоновки. Он не заменяет физическое устройство. Не записывайте субъективное ощущение скорости запуска как числовой показатель: без отдельного измерения это не доказательство производительности.
Ответы на точечные вопросы
Почему SwiftUI всё ещё получает предупреждение, если plist создаётся автоматически?
Потому что автоматическая генерация может выполняться для другого Target или другой Configuration. Проверьте Release Archive, значение INFOPLIST_FILE, настройки генератора и Run Script. Если ключ появился только в Debug, сервер его не увидит.
Что означает ITMS-90870 Missing launch screen на практике?
Это означает, что загруженный пакет не содержит распознанной Apple декларации экрана запуска в том виде, который требуется для сборки с iOS 27 SDK или выше. Сверяйте не исходный файл, а Info.plist внутри конечного .app.
Нужно ли переходить на UILaunchScreen, если уже есть storyboard?
Не всегда. Исправный storyboard можно сохранить. Переход оправдан, когда текущая схема сложна, нестабильна или теряется при генерации проекта. Главное — один согласованный способ, реальный ресурс и проверка архива.
Можно ли отправить сборку Xcode 27 Beta в App Store?
Подтверждена загрузка Xcode 27 Beta 5 в TestFlight. Это не подтверждает разрешение на production-публикацию в App Store. Для такой операции сверяйте текущие требования App Store Connect и статус конкретной версии Xcode.
TestFlight как контрольный серверный тест
После локальной проверки загрузите новый Archive в TestFlight через поддерживаемый канал. Инструкция Apple по загрузке сборок описывает базовый процесс, а справочник статусов сборок помогает отличить ошибку обработки от ошибки самой загрузки.
Запишите в журнал:
- версию Xcode;
- SDK, использованный для Archive;
- Scheme и Configuration;
- дату создания архива;
- обезличенный Bundle ID;
- результат проверки ITMS-90870;
- статус обработки TestFlight.
Если ошибка исчезла, это подтверждает прохождение серверной проверки именно для загруженного бинарного файла. Но перед production-релизом повторно проверьте, что используется тот же Scheme, та же Configuration и тот же вход Archive. Нельзя считать локальный успех доказательством, если автоматический сборщик формирует другой пакет.
Для удалённого Mac порядок такой же. Важно закрепить версию Xcode, путь к проекту, способ авторизации, переменные CI/CD и секреты подписи. В интерактивной сессии и в автоматическом Archive должен проверяться один и тот же конечный .app, а не только исходная ветка.
Итоговый чек-лист перед переключением
- [ ] Определён SDK, которым создан текущий Release Archive.
- [ ] Установлено, что проверка относится к SDK, а не только к Deployment Target.
- [ ] Для основного App Target выбран допустимый способ декларации.
- [ ]
UILaunchScreenзаполнен илиUILaunchStoryboardNameуказывает на существующий файл. - [ ] Storyboard или другие ресурсы входят в нужный Target.
- [ ] Проверены все Release-конфигурации и Scheme.
- [ ] Проверены скрипты, которые могут перезаписывать
Info.plist. - [ ] Создан новый Release Archive после исправления.
- [ ] Проверен
Info.plistвнутри.app. - [ ] Проверено наличие связанного ресурса в пакете.
- [ ] Приложение удалено и заново запущено на симуляторе.
- [ ] Выполнена проверка на физическом iPhone или iPad.
- [ ] Сборка загружена в TestFlight.
- [ ] В журнале сохранены версия Xcode, SDK и результат обработки.
- [ ] Для production зафиксирована обратная точка на цепочку iOS 26 SDK, если переход ещё не завершён.
Текущий сборщик и независимый Mac для приёмки
Если существующий Windows/Linux-процесс или старый локальный Mac выполняет только текущую iOS 26-сборку, немедленная замена оборудования не обязательна. Но у такого подхода есть реальные ограничения: нельзя независимо проверить Xcode 27, окружение может не поддерживать нужную версию SDK, а изменение скриптов способно нарушить стабильный production-процесс. При удалённом сборщике добавляются задержки передачи проекта, зависимость от сетевого доступа и риск расхождения между интерактивным и автоматическим Archive.
В такой ситуации разумно отделить проверку миграции от основной машины. Описание доступных вариантов MESHLAUNCH можно рассматривать как способ получить отдельное macOS-окружение для Xcode 27 Archive и TestFlight, не меняя сразу стабильный production-сборщик. Если после проверки понадобится постоянная схема, сравните её с арендой Mac mini, учитывая требования к доступу, сроку работы и физическим интерфейсам.
Аренда не заменяет локальный Mac для команды, которой нужен длительный тяжёлый workload, постоянное подключение физических устройств или гарантированный офлайн-доступ. Для временной совместимости, независимого Archive и обратимой проверки перед переходом на iOS 27 она практичнее, чем в последний день менять всю производственную цепочку.