Во Flutter начиная с версии 3.44 Swift Package Manager используется по умолчанию для управления нативными зависимостями iOS и macOS, а несовместимые зависимости могут продолжать работать через CocoaPods — это подтверждает официальная документация Flutter по миграции на SwiftPM.
Симптом: после обновления Flutter меняются файлы iOS-проекта, а часть плагинов перестаёт собираться или ломает Archive.
Самое быстрое решение: не удаляйте CocoaPods сразу; оставьте его резервным контуром, перенесите совместимые плагины в Swift Package Manager, а производственный Mac переключайте только после успешной проверки Archive и TestFlight.
Кому нужен этот план миграции
Статья предназначена для независимых разработчиков, которые переходят со старой версии Flutter на 3.47 и не могут допустить остановку публикации iOS-приложения.
Она также пригодится владельцам проектов с несколькими нативными плагинами, если их совместимость со Swift Package Manager ещё не подтверждена. Отдельно этот сценарий подходит небольшим командам, которые пишут код в Windows или Linux, а сборку и публикацию выполняют на удалённом Mac.
Последняя проверка материала выполнена 17 сентября 2026 года. Факты сверены с примечаниями к релизам Flutter, документацией SwiftPM и инструкциями Apple по загрузке сборок.
До миграции зафиксируйте рабочую точку
Не начинайте с команды, которая пересоздаёт iOS-проект или удаляет Podfile. Сначала вам нужна доказуемая точка возврата: состояние, из которого можно снова выпустить уже опубликованную версию приложения.
Сохраните в отдельной ветке Git и в защищённом архиве:
- вывод
flutter --versionи путь к используемому Flutter SDK; pubspec.yamlи файл блокировки зависимостей;- каталог
ios, включаяPodfile,Podfile.lock, локальные Pod и пользовательские скрипты; - выбранную версию Xcode и результат команды
xcode-select -p; - способ подписи, название команды разработчика и тип профиля, но без публикации секретов;
- последний успешный Archive и параметры экспорта IPA;
- номер версии, номер сборки и запись об успешной обработке в App Store Connect.
Копии сертификатов, приватных ключей и токенов App Store Connect не следует хранить рядом с кэшем SwiftPM или в репозитории. Для удалённого Mac это особенно важно: сбой сеанса не должен одновременно уничтожить исходники, подпись и доказательства последней рабочей сборки.
Почему обновление Flutter 3.47 меняет iOS-проект автоматически? Потому что переход на Swift Package Manager затрагивает способ подключения нативных плагинов и связанные с ним файлы проекта. Такие изменения не обязательно означают ошибку. Сравните diff после обновления и проверьте, какие файлы были добавлены, изменены или сгенерированы заново. Официальные ограничения и поведение миграции описаны в руководстве Flutter для разработчиков приложений.
Пошаговая подготовка
- Создайте ветку
flutter-347-spm-migration, не меняя production-ветку. - Подготовьте отдельный Mac или отдельный пользовательский каталог сборки.
- Зафиксируйте путь к Flutter SDK, Xcode и проекту, например
/Users/build/flutterи/Users/build/app. - Скопируйте журнал последнего успешного
flutter build ipa. - Проверьте, что старый контур действительно собирается без незапланированного обновления зависимостей.
- Назначьте условие отмены: любая ошибка подписи, повторное подключение одной библиотеки или неизвестное изменение Archive возвращает вас к сохранённой ветке.
В первый час составьте карту нативных плагинов
Обычного Debug-запуска недостаточно, чтобы объявить миграцию успешной. Он может не проходить через Release-скрипты, экспорт IPA, подпись или отдельную ветку нативного кода. Сначала составьте перечень всех зависимостей, которые выходят за пределы Dart.
Начните с дерева зависимостей:
# Учётная запись: build
# Каталог проекта: /Users/build/app
cd /Users/build/app
/Users/build/flutter/bin/flutter pub deps --style=compact
Ожидаемый результат — список прямых и транзитивных пакетов. В отдельную строку вынесите плагины, в которых есть ios/, Swift, Objective-C, .framework, .xcframework, Podspec или пользовательский Build Phase. Команда не доказывает совместимость со SwiftPM, но помогает не пропустить зависимость, которая подключается не напрямую.
Затем проверьте проектный каталог и ручные настройки:
- есть ли собственные строки в
Podfile; - используется ли локальный Pod или приватный Spec;
- подключается ли бинарный Framework вручную;
- присутствует ли скрипт, который ожидает
Pods/; - задаётся ли минимальная версия iOS отдельно для разных зависимостей;
- не копируется ли один и тот же Framework одновременно из SwiftPM и CocoaPods.
Для каждого элемента укажите один из статусов:
- подтверждена интеграция через SwiftPM;
- временно остаётся в CocoaPods;
- источник или механизм подключения неясен;
- зависимость нужно заменить или проверить у сопровождающего.
Сведения о том, как Flutter-плагины объявляют нативные платформы и пакеты, можно сверить с документацией по разработке Flutter-плагинов. Не переносите плагин только потому, что его Debug-сборка завершилась успешно.
Первый запуск SwiftPM выполняйте без публикации
После обновления Flutter получите зависимости и откройте именно рабочую область, если она создаётся проектом:
# Учётная запись: build
# Проект: /Users/build/app
# Flutter SDK: /Users/build/flutter
cd /Users/build/app
/Users/build/flutter/bin/flutter pub get
open ios/Runner.xcworkspace
Если рабочая область или файл проекта изменились, сначала просмотрите diff и список пакетов в Xcode. Ищите три видимых результата:
- Swift-пакеты разрешились без ошибки версии;
- плагины зарегистрированы в проекте;
- на этапе компиляции не появились отсутствующие модули или повторные символы.
Первую проверку делайте без подписи на симуляторе либо в базовой конфигурации, где не затрагивается публикационный сертификат. Это не заменяет Archive: цель этапа — отделить проблему разрешения пакетов от проблемы кодовой подписи.
Пример команды:
# Учётная запись: build
# Каталог проекта: /Users/build/app
cd /Users/build/app
/Users/build/flutter/bin/flutter build ios --simulator
Ожидаемый результат — завершение компиляции симуляторной версии без ошибок SwiftPM, регистрации плагинов и линковки. Если команда завершается ошибкой, сохраните полный лог, не повторяйте бесконечно pub get и не удаляйте файлы кэша до копирования доказательств.
Откат на этом этапе выполняется через Git:
# Учётная запись: build
# Каталог проекта: /Users/build/app
git status
git diff -- ios
git restore -- ios
git switch production-baseline
Названия веток здесь приведены как пример; замените их на свои обезличенные имена. Bundle ID, Team ID, названия сертификатов и пути к секретам не должны попадать в публичный журнал.
Двухконтурная схема предотвращает рискованный «переезд за ночь»
Что делать, если Flutter-плагин пока не поддерживает Swift Package Manager? Оставьте его в CocoaPods, если он нужен приложению и его текущая интеграция подтверждена последним рабочим Archive. Не переписывайте исходный код стороннего плагина только ради чистого SwiftPM: это создаёт собственную ветку сопровождения и усложняет следующий апгрейд.
Временное сосуществование SwiftPM и CocoaPods допустимо, когда разные плагины используют разные системы. Недопустимо, чтобы одна и та же нативная библиотека приходила из обоих источников. В этом случае типичные симптомы — повторные символы на линковке, модуль не найден, несовместимая минимальная версия iOS или разные варианты одного Framework.
Используйте такую последовательность разбора:
- Повторные символы: найдите библиотеку в списке Swift-пакетов, Podfile и Build Phases; удалите один источник, а не меняйте случайно порядок линковки.
- Модуль не найден: проверьте имя продукта SwiftPM, регистрацию плагина и наличие нужной конфигурации Release.
- Конфликт минимальной версии iOS: сравните требования каждой зависимости и выберите совместимый вариант либо временно верните проблемный пакет в CocoaPods.
- Не удалось скачать пакет: сохраните лог, проверьте доступ удалённого Mac к репозиторию и повторите разрешение в чистой среде; кэш не должен быть единственным доказательством работоспособности.
- Работает Debug, но ломается Archive: исследуйте Release-настройки, скрипты фаз сборки, подпись и экспорт, а не только код плагина.
Важно: кэш SwiftPM ускоряет повторную сборку, но не подтверждает воспроизводимость. Минимум один раз проверьте разрешение зависимостей после очистки рабочего каталога или на отдельном Mac.
Можно ли использовать SwiftPM и CocoaPods в одном Flutter-проекте? Да, если каждый нативный компонент подключён ровно одним способом и вы понимаете, где он появляется в итоговом Xcode-проекте. Такая схема не обязана быть финальной архитектурой; её задача — сохранить публикацию, пока конкретные плагины проходят проверку.
Удалённый Mac должен повторять production-инструменты
Если код пишется в Windows или Linux, проверка iOS-зависимостей всё равно должна проходить на том же типе macOS-окружения, где выполняется публикация. Локальная Dart-проверка не показывает, как Xcode разрешит Swift-пакеты, подпишет приложение и экспортирует IPA.
Зафиксируйте в инструкции сборки:
- путь к Flutter SDK;
- выбранный Xcode через
xcode-select; - учётную запись, от имени которой запускается unattended-задача;
- каталог проекта и права на него;
- файлы блокировки зависимостей;
- способ доступа к сертификатам и профилям;
- отдельное расположение кэша SwiftPM;
- порядок восстановления после обрыва VNC, SSH или веб-сеанса.
Не смешивайте кэш зависимостей с ключами подписи. На удалённом Mac сначала восстановите инструментальную цепочку, затем зависимости, затем секреты в рамках защищённой процедуры. Если сессия оборвалась во время разрешения пакетов, начинайте с проверки состояния процесса и журнала, а не с повторного запуска нескольких параллельных сборок.
Для небольшой команды полезно заранее описать два режима:
- проверочный: чистый каталог, разрешение пакетов, симуляторная сборка;
- публикационный: фиксированные зависимости, Release, Archive, экспорт IPA и загрузка.
Инструкции по общей последовательности сборки и публикации Flutter-приложения находятся в официальном руководстве Flutter для iOS. Если вам требуется отдельная машина именно для такой проверки, сначала оцените требования к удалённому рабочему месту в материале о конфигурации Mac mini для удалённой разработки.
Archive и TestFlight — критерий готовности, а не формальность
После базовой сборки переходите к публикационной проверке. Выполняйте действия раздельно и сохраняйте журнал каждого этапа:
- Увеличьте номер сборки в согласованной ветке, не меняя Bundle ID и схему подписи.
- Выполните Release-сборку с теми же зависимостями, которые прошли симуляторную проверку.
- Создайте Archive в Xcode и убедитесь, что все нативные плагины присутствуют в итоговом приложении.
- Экспортируйте IPA выбранным способом подписи.
- Загрузите сборку в App Store Connect.
- Дождитесь обработки и проверьте статус сборки в интерфейсе.
- Установите её через TestFlight на тестовое устройство.
- Проверьте функции, которые используют нативные плагины: уведомления, платежи, авторизацию, работу с файлами, камеру или фоновые задачи — только те, что есть в вашем приложении.
- Сравните результат с последней рабочей версией.
- Оставьте старый контур доступным до завершения проверки.
Правила загрузки сборок и допустимые способы передачи IPA описаны в справке Apple по загрузке сборок. Статус «загружено» ещё не равен доступности для тестирования: сверяйте обработку в App Store Connect по официальному справочнику статусов сборок.
Как откатить SwiftPM, если Archive не проходит? Не удаляйте случайные пакеты из Xcode и не меняйте сертификаты одновременно с зависимостями. Вернитесь к сохранённой ветке и рабочему lock-файлу, восстановите CocoaPods для проблемного плагина, повторите Archive и сравните журнал с последней успешной публикацией. Если старый контур не собирается, сначала восстановите его отдельно; только после этого продолжайте расследовать SwiftPM.
Считайте миграцию принятой, когда новый контур прошёл Release, Archive, экспорт, обработку в App Store Connect и реальную установку через TestFlight. Один успешный Debug-запуск для такого решения недостаточен.
Условия окончательного переключения
После TestFlight выберите один из трёх вариантов:
- полностью перейти на SwiftPM, если все нативные зависимости подтверждены и чистая среда повторяет результат;
- оставить двухконтурную схему, если CocoaPods нужен одному или нескольким плагинам;
- отложить миграцию, если проблема связана с неподдерживаемой зависимостью, неизвестным приватным Spec или нестабильным Release-процессом.
Не устанавливайте дату перехода только потому, что новая схема выглядит современнее. Зафиксируйте конкретное условие повторной проверки: выход совместимой версии плагина, исправление ошибки линковки или успешная сборка в чистой среде.
CocoaPods остаётся рабочим резервом, но его долгосрочный статус нужно учитывать: Flutter указывает, что реестр CocoaPods должен перейти в режим только для чтения 2 декабря 2026 года. Это подтверждённая дата изменения инфраструктуры, а не доказательство того, что каждый плагин к этому моменту будет совместим со SwiftPM. Совместимость проверяйте по каждому проекту.
Решение по условиям, а не по принципу «перенести всё»
Используйте следующий порядок выбора:
- Если плагин имеет подтверждённую интеграцию со SwiftPM, его можно переносить в первую очередь; после этого нужна Release-проверка.
- Если плагин работает только через CocoaPods и критичен для приложения, оставьте его в резервном контуре.
- Если одна библиотека появляется из двух источников, остановите миграцию и устраните дублирование.
- Если проблема видна только при Archive, не считайте симулятор доказательством; возвращайтесь к Release-настройкам и подписи.
- Если чистая среда не может разрешить зависимости, не объявляйте кэш постоянным решением.
- Если TestFlight устанавливает приложение, но нативная функция не работает, переключение production-цепочки запрещено до исправления функции.
- Если единственный Mac одновременно используется для текущих релизов и эксперимента, вынесите миграцию на изолированную машину.
Ниже — итоговая оценка вариантов. Она не заменяет техническую проверку, но помогает выбрать следующий шаг.
| Вариант | Совместимость плагинов | Риск для текущего релиза | Когда выбирать | Оценка для миграции |
|---|---|---|---|---|
| Полный SwiftPM | Все нативные зависимости подтверждены | Низкий после TestFlight | Чистая среда и Archive повторяют результат | 5/5 |
| SwiftPM + CocoaPods | Разные плагины используют разные системы | Средний, нужен контроль дублирования | Есть несовместимый, но необходимый плагин | 4/5 |
| Временный CocoaPods | SwiftPM вызывает ошибки или неясен источник пакета | Низкий для текущего релиза, выше для будущих изменений | Нужно срочно выпустить исправление | 3/5 |
| Принудительная правка чужого плагина | Совместимость не подтверждена | Высокий | Только при наличии собственного сопровождения и тестов | 1/5 |
Сравнение среды проверки и затрат на ошибки
Для миграции важна не только стоимость Mac, но и цена недоступного production-процесса. Удалённая среда полезна, когда вам нужно отделить эксперимент от машины, на которой уже выполняются публикационные задачи. Требования к доступу, сроку использования и способу подключения следует сверять с актуальными условиями аренды Mac через SSHMac, а не переносить характеристики из сторонних предложений.
| Среда | Сильная сторона | Ограничение | Роль в миграции Flutter 3.47 | Оценка |
|---|---|---|---|---|
| Единственный локальный Mac | Быстрый доступ к устройству и ключам | Ошибка меняет единственный production-контур | Только после резервного копирования | 3/5 |
| Отдельный локальный Mac | Хорошая изоляция и физический доступ | Нужно покупать и обслуживать дополнительное оборудование | Подходит для длительной миграции | 4/5 |
| Удалённый Mac | Быстрое отделение эксперимента от текущего релиза | Нужны стабильное подключение и процедура восстановления | Подходит для временной проверки и команды без Mac | 4/5 |
| CI-среда без интерактивной проверки | Повторяемые автоматические задачи | Сложнее исследовать Xcode и нативные плагины | Финальный этап после ручной валидации | 3/5 |
Не путайте удалённый Mac с заменой всех процессов CI. Он даёт полноценную macOS-среду для миграции, но вам всё равно нужно зафиксировать команды, секреты, доступ и порядок восстановления.
Финальная карта релиза
Перед переключением production выполните контрольный список. Он должен быть привязан к конкретным артефактам, а не к устному подтверждению:
| Проверка | Доказательство | Если не выполнено |
|---|---|---|
| Flutter SDK и Xcode зафиксированы | Запись версий и путь к инструментам | Остановить переключение |
| Зависимости разрешаются в чистом каталоге | Лог SwiftPM и lock-файлы | Оставить текущий контур |
| Нет двойного подключения Framework | Список пакетов, Pods и Build Phases | Устранить источник дублирования |
| Release-сборка завершается | Лог Flutter/Xcode | Вернуться к последней рабочей ветке |
| Archive и экспорт IPA успешны | Archive и экспортированный артефакт | Не менять production |
| App Store Connect принял сборку | Статус обработки | Проверить загрузку и подпись |
| TestFlight устанавливает приложение | Тестовое устройство и журнал функций | Исправить нативный плагин |
| Старый путь восстановления сохранён | Ветка, lock-файлы и инструкция | Отложить финальный переход |
Такой список также помогает команде, которая разрабатывает в Windows: код можно продолжать писать локально, но проверка iOS-зависимостей, Archive и TestFlight должна выполняться на контролируемом Mac. Общий процесс App Store Connect и роли этапов публикации описаны в официальной схеме рабочего процесса Apple.
Если сейчас вы собираете приложение на единственном Mac, переносите Flutter 3.47 прямо в его рабочую цепочку или пытаетесь проверить iOS из Windows/Linux без отдельной macOS-среды, у текущего подхода есть три очевидных недостатка: миграция может остановить ближайший релиз, эксперимент конкурирует за сертификаты и кэш с production, а удалённое восстановление после ошибки остаётся неописанным. Временная аренда Mac через SSHMac позволяет вынести двухконтурную проверку на отдельную машину, сохранить старый путь публикации и вернуть результат в основную цепочку только после Archive и TestFlight. Для разового перехода или короткого периода параллельной проверки это обычно рациональнее, чем сразу превращать неподтверждённую миграцию в постоянную инфраструктуру.