Симптом: после обновления Flutter 3.47 прежний iOS-проект больше не проходит Build или Archive.
Быстрое решение: сначала перейдите на последний стабильный патч Flutter 3.47, затем повторите проверку SwiftPM на том же коммите и только после этого рассматривайте временный откат к CocoaPods.
Эта последовательность подходит, если ошибка появилась после обновления Flutter или Xcode 27, а также если локальная сборка проходит, но удалённый Mac завершается с ошибкой. Не удаляйте сразу все кэши, Pod-конфигурацию или рабочую машину: сначала сохраните доказательства и установите границу сбоя.
Точка отсчёта
Представьте обезличенный журнал независимого проекта. Один и тот же коммит собирался в прежнем окружении. После обновления Flutter 3.47 и Xcode 27 команда получила ошибку на iOS Build. В конце лога оставалось только общее сообщение о завершении команды, поэтому первоначальная попытка очистить кэши не дала полезного результата.
В такой ситуации последняя строка не является диагнозом. Вам нужны:
- фактический патч Flutter: например, 3.47.0, 3.47.1, 3.47.2 или более поздний;
- версия Xcode и активный путь к
xcode-select; - команда, которая запускается:
flutter build ios,xcodebuild, Archive или CI-скрипт; - первый содержательный сбой, а не итоговый код завершения;
- состояние разрешения зависимостей;
- отдельный результат обычного Build и Archive.
На 14 сентября 2026 года Xcode 27 был официально выпущен Apple. Это не означает, что любая ошибка после обновления связана с самим Xcode. В официальном changelog Flutter отдельно указаны исправления для iOS/macOS-сборки при включённом SwiftPM, а также для нативных iOS Add-to-App проектов, где Flutter Swift packages не собирались под Xcode 27. Эти исправления относятся к Flutter 3.47.2; в changelog также перечислен последующий патч 3.47.3. Проверяйте актуальное состояние в официальном changelog Flutter, а сведения о выпуске — в материалах Apple по Xcode 27.
Не смешивайте три события:
- разрешение Swift Package;
- компиляцию Xcode Target;
- создание подписанного Archive.
Каждое может завершиться по разной причине.
Исправленный патч
Первое изменение должно быть небольшим и обратимым. Создайте отдельную ветку или сохраните текущий SDK через используемый вами менеджер версий. Затем установите последний доступный стабильный патч Flutter 3.47, не меняя одновременно исходный код, плагины и команду сборки.
Последовательность выглядит так:
- Запишите текущий вывод
flutter --version. - Запишите
xcodebuild -versionиxcode-select -p. - Сохраните
pubspec.lock,Package.resolved,Podfile.lockи изменения вios/. - Переключитесь на исправленный стабильный патч Flutter 3.47.
- Выполните
flutter pub get, не меняя список зависимостей. - Повторите разрешение SwiftPM и обычный Build.
- Сравните первый полезный сбой с журналом старой версии.
Если после обновления одновременно меняются Flutter, Xcode, плагины и Podfile, вы теряете контрольный эксперимент. Если новая ошибка исчезла, всё равно зафиксируйте, какое изменение её устранило.
Проверяйте также канал Flutter. Стабильный патч предпочтительнее непроверенной промежуточной сборки, если вам нужно восстановить выпускной процесс. Сведения о конкретных исправлениях берите из changelog, а не из общего обсуждения Issue. Сообщество может показать характерный текст ошибки, но отдельный Issue не доказывает, что именно он объясняет ваш проект.
Интеграция SwiftPM
После патч-обновления проверьте, как Flutter подключён к iOS-проекту. Документация Flutter по интеграции Swift Package Manager описывает отдельные элементы, которые должны присутствовать в проекте. Ищите не только сам пакет, но и связь с нужным Target.
Проверьте следующие признаки:
- в проекте присутствует
FlutterGeneratedPluginSwiftPackage, если его должна создавать текущая интеграция; - нужный Target действительно зависит от Flutter package и продуктов плагинов;
- перед сборкой выполняется необходимый Flutter-скрипт;
- нет одновременно двух неполных вариантов подключения одного плагина;
Package.resolvedсоответствует коммиту и доступен пользователю, запускающему сборку;- рабочий каталог не меняется между разрешением зависимостей и
xcodebuild.
Три типа проектов требуют разной проверки.
Обычное Flutter-приложение. Начните с генерируемой iOS-структуры, команды Flutter и списка плагинов. Ручные изменения в ios/ особенно подозрительны, если они появились до перехода на SwiftPM.
Нативный iOS Add-to-App. Здесь Flutter встраивается в уже существующий проект. Используйте официальную документацию Flutter по Add-to-App, а не инструкцию для обычного приложения. Target хоста, Flutter-модуль и пакет плагинов могут иметь разные зависимости.
Проект с собственными Target. Приложение, расширение, тестовый Target или внутренний инструмент могут не получать ту же интеграцию, что основной App Target. Проверяйте каждый Target отдельно. Успешная сборка приложения не доказывает, что расширение или Archive настроены корректно.
Swift Package Manager разрешает пакеты и передаёт продукты в проект, но не исправляет несовместимый плагин автоматически. Поэтому после проверки структуры переходите к цепочке зависимостей.
Плагины и временный откат
Если исправленный Flutter не помог, составьте список плагинов, которые участвуют в iOS-сборке. Для каждого укажите:
- версию в lock-файле;
- наличие нативной iOS-части;
- заявленную поддержку SwiftPM;
- собственные зависимости;
- минимальную версию iOS;
- изменения, внесённые вручную в Podfile или Xcode project.
Flutter может использовать смешанную цепочку. Один плагин уже подключается через SwiftPM, другой всё ещё требует CocoaPods. Ошибка возникает не потому, что один менеджер «плохой», а потому, что конкретная зависимость или Target не соответствует выбранной схеме.
Проверьте два независимых пути:
- разрешение SwiftPM и подключение Package Product;
- установку Pod-зависимостей и создание рабочих Pod-конфигураций.
Не удаляйте Podfile только потому, что SwiftPM включён. Если несовместимый плагин всё ещё нужен проекту, удаление рабочей Pod-цепочки может создать новую проблему и усложнить откат.
Временный CocoaPods fallback оправдан, когда есть подтверждённая граница:
- конкретный плагин не поддерживает SwiftPM;
- приватная нативная библиотека поставляется только как Pod;
- текущий Target не может получить необходимый Swift Package Product;
- исправленный Flutter и корректная интеграция уже проверены, но зависимость остаётся несовместимой.
Перед откатом сохраните копию Podfile, lock-файла и проектных изменений. Зафиксируйте, какие плагины переведены назад. Не смешивайте эту проверку с обновлением минимальной версии iOS.
Полезно свериться с первоисточником Swift Package Manager, но не переносите общие рекомендации SwiftPM на Flutter-проект без проверки документации Flutter. У Flutter есть собственные скрипты и правила генерации.
Выпускной контур
Успешный Debug Build подтверждает только часть цепочки. Для публикации вам нужно пройти несколько самостоятельных этапов:
- Разрешить зависимости на чистом или контролируемом рабочем каталоге.
- Собрать Debug, чтобы быстро исключить базовую ошибку Target.
- Собрать Release с тем же коммитом.
- Создать Archive в Xcode или через эквивалентную команду.
- Экспортировать Archive с нужным методом распространения.
- Проверить подпись, профиль и включённые возможности.
- Запустить фактическую загрузку в App Store Connect, если это входит в вашу процедуру.
Сведения о поведении среды берите из официальных Xcode Release Notes. Материалы Apple о новых возможностях Xcode 27 полезны для проверки изменений инструментария, но не заменяют диагностику вашего Target.
Если локально всё проходит, а удалённый Mac падает, сравните способ запуска. Графическая сессия и SSH-команда могут видеть разные переменные, Keychain-состояние, рабочий каталог и права на кэш. Удалённая сборка может также использовать другой xcode-select, другой Flutter SDK или другой пользовательский профиль.
Проверьте по порядку:
- путь к Flutter и Xcode в автоматическом сеансе;
- права на каталог проекта и кэш пакетов;
- доступ к Git-источникам приватных зависимостей;
- наличие
Package.resolvedиPodfile.lock; - загрузку сертификатов и профилей в нужную Keychain-сессию;
- одинаковый метод Archive и экспорта;
- отсутствие интерактивного окна, ожидающего действия пользователя.
Для удалённого Mac важно не считать разрыв соединения ошибкой проекта. Повторите только тот этап, который завершился сбоем, затем выполните полный Archive. Если после переподключения состояние меняется, исследуйте сохранение сеанса, права и расположение кэша.
Контрольная неделя
После первого успешного Archive не переключайте сразу производственный контур. Закрепите:
- версию Flutter и канал;
- версию Xcode;
- версии плагинов;
pubspec.lock,Package.resolvedиPodfile.lock;- команду разрешения зависимостей;
- команду Build;
- команду Archive и экспорта;
- требования к Keychain и переменным окружения.
Создайте минимальную регрессионную задачу. Она должна пройти разрешение зависимостей, обычный Build и Archive на том же коммите. Для проекта Add-to-App добавьте проверку нативного Host Target. Для проекта со смешанными зависимостями отдельно проверяйте SwiftPM и оставшуюся CocoaPods-цепочку.
После этого выполните три эксплуатационные проверки:
- разрыв удалённого подключения и повторное подключение;
- перезапуск хоста;
- реальный выпускной Archive, а не только тестовую Debug-сборку.
Если после перезапуска меняется результат, проблема ещё не закреплена. Если работает только старый Flutter, временно сохраните двойной контур: старую версию для выпуска и новую для контролируемой проверки. Не переводите рабочие публикации на новый SDK до повторного Archive после следующего изменения.
Проверочный список
Перед закрытием инцидента отметьте каждый пункт:
- [ ] Сохранены Flutter, Xcode, команда сборки и первый полезный текст ошибки.
- [ ] Подтверждено, что сбой появился после конкретного обновления.
- [ ] Установлен последний стабильный патч Flutter 3.47 с нужными исправлениями.
- [ ] Тот же коммит повторно прошёл разрешение зависимостей.
- [ ] Проверена структура
FlutterGeneratedPluginSwiftPackage. - [ ] Отдельно проверен тип проекта: обычный Flutter, Add-to-App или собственные Target.
- [ ] Для каждого нативного плагина подтверждена поддержка SwiftPM либо документирована причина отката.
- [ ] Podfile и lock-файлы не удалялись без сохранённой копии.
- [ ] Debug Build, Release и Archive проверены раздельно.
- [ ] Экспорт и подпись проверены в автоматическом сеансе.
- [ ] Локальная и удалённая команды используют одинаковые версии инструментов.
- [ ] После переподключения и перезапуска удалённого Mac выполнена повторная проверка.
Если хотя бы один пункт не отмечен, инцидент лучше считать незакрытым. В частности, успешный Build без Archive не подтверждает готовность публикации.
Частые сценарии
Flutter 3.47.2 исправил ошибку, но проект всё ещё не собирается
Это не доказывает, что причина была только в известной регрессии. Проверьте плагины, собственные Target и ручные изменения в Podfile. Затем сравните чистое минимальное приложение с обезличенным проектом, где есть реальные зависимости. Если ломается только второй вариант, ищите границу в плагине или его нативной части.
SwiftPM работает, но Archive не создаётся
Разделите ошибку разрешения пакетов и ошибку выпуска. Если все пакеты разрешились, но Archive падает, проверьте Release-конфигурацию, Target-зависимости, подпись и экспорт. Перезапуск SwiftPM-кэша не исправит неправильную связь продукта пакета с Target.
Локальный Mac проходит, удалённый — нет
Повторите сборку через тот же SSH или CI-вход. Сравните PATH, xcode-select, домашний каталог, права, Keychain и доступ к приватным репозиториям. Если графическая Xcode-сессия успешна, это ещё не подтверждает готовность безымянного автоматического процесса.
Нужен старый Flutter для выпуска
Оставьте старую версию в отдельном воспроизводимом контуре, но не удаляйте новую. Зафиксируйте, какой этап требует отката: разрешение пакетов, Build, Archive или экспорт. Это позволит перейти на исправленный патч без повторного расследования.
Выбор среды
Если проблема требует одновременно держать старую и новую связку Flutter/Xcode, локальный компьютер быстро превращается в хрупкий переключаемый стенд. Windows или Linux также не закрывают финальный iOS Archive без доступного macOS-окружения. Покупка отдельного Mac оправдана, когда вам нужен постоянный физический доступ, длительные тяжёлые задачи или собственные подключаемые устройства.
Если вы рассматриваете постоянный физический стенд, заранее сопоставьте его с условиями аренды Mac mini для разработки. Такой вариант позволяет проверить реальный рабочий процесс без немедленного изменения основного компьютера: установите нужные версии Flutter и Xcode, выполните Archive, проверьте восстановление после перезапуска и только затем решайте, какой контур оставить.
Для короткого периода исправления, выпуска или проверки удалённый Mac позволяет сохранить отдельную среду с root-доступом и не смешивать её с рабочим компьютером. На странице удалённого Mac для iOS-сборки можно сначала оценить такой вариант как временный контур, а затем проверить реальный Archive своим проектом.
У MACCOME аренда не заменяет диагностику проекта. Она лишь даёт отдельный macOS-хост, где можно закрепить Flutter, Xcode, lock-файлы и команды. Это полезнее, чем пытаться лечить одновременно исходный код и постоянно меняющееся локальное окружение. Но для постоянной высокой нагрузки, физического USB-доступа или требований к собственной инфраструктуре покупка и самостоятельное обслуживание Mac могут быть разумнее.
Начните с воспроизводимой проверки: исправленный патч, тот же коммит, SwiftPM, Archive и автоматический запуск. Если все этапы проходят, сохраните конфигурацию и только потом решайте, нужен ли вам временный или постоянный удалённый Mac-контур.