Во 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 для разработчиков приложений.

Пошаговая подготовка

  1. Создайте ветку flutter-347-spm-migration, не меняя production-ветку.
  2. Подготовьте отдельный Mac или отдельный пользовательский каталог сборки.
  3. Зафиксируйте путь к Flutter SDK, Xcode и проекту, например /Users/build/flutter и /Users/build/app.
  4. Скопируйте журнал последнего успешного flutter build ipa.
  5. Проверьте, что старый контур действительно собирается без незапланированного обновления зависимостей.
  6. Назначьте условие отмены: любая ошибка подписи, повторное подключение одной библиотеки или неизвестное изменение 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.

Для каждого элемента укажите один из статусов:

  1. подтверждена интеграция через SwiftPM;
  2. временно остаётся в CocoaPods;
  3. источник или механизм подключения неясен;
  4. зависимость нужно заменить или проверить у сопровождающего.

Сведения о том, как 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 — критерий готовности, а не формальность

После базовой сборки переходите к публикационной проверке. Выполняйте действия раздельно и сохраняйте журнал каждого этапа:

  1. Увеличьте номер сборки в согласованной ветке, не меняя Bundle ID и схему подписи.
  2. Выполните Release-сборку с теми же зависимостями, которые прошли симуляторную проверку.
  3. Создайте Archive в Xcode и убедитесь, что все нативные плагины присутствуют в итоговом приложении.
  4. Экспортируйте IPA выбранным способом подписи.
  5. Загрузите сборку в App Store Connect.
  6. Дождитесь обработки и проверьте статус сборки в интерфейсе.
  7. Установите её через TestFlight на тестовое устройство.
  8. Проверьте функции, которые используют нативные плагины: уведомления, платежи, авторизацию, работу с файлами, камеру или фоновые задачи — только те, что есть в вашем приложении.
  9. Сравните результат с последней рабочей версией.
  10. Оставьте старый контур доступным до завершения проверки.

Правила загрузки сборок и допустимые способы передачи 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. Для разового перехода или короткого периода параллельной проверки это обычно рациональнее, чем сразу превращать неподтверждённую миграцию в постоянную инфраструктуру.