Обновление Flutter 3.47 для iOS не следует выполнять непосредственно на производственном узле: сначала создайте изолированный удалённый Mac, сохраните рабочую конфигурацию и оставьте CocoaPods как путь отката, пока не пройдены проверки реального устройства, Archive, подписи и CI. Этот порядок подходит проектам с нативными плагинами, Flavor, собственными Target и интеграцией add-to-app.

Кому нужен этот материал: разработчикам Flutter, которые отвечают за iOS-релизы и должны понять, изменится ли способ разрешения зависимостей. DevOps-инженерам он помогает подготовить воспроизводимый узел сборки, а владельцам add-to-app — не смешать новую интеграцию со старым подключением Flutter-модуля.

Последняя проверка: 21 августа 2026 года. Сведения о стабильной ветке Flutter и переходе к Swift Package Manager сверены с индексом официальных релизов Flutter, руководством по SPM и материалами Flutter 3.44. Требования конкретных плагинов и Xcode необходимо дополнительно проверять в день миграции.

01

Карта миграционных сценариев

Официальная документация Flutter указывает, что Flutter 3.47 относится к стабильным релизам, а начиная с Flutter 3.44 Swift Package Manager используется по умолчанию для нативных зависимостей iOS и macOS. Это не означает автоматического удаления всех Podfile или немедленной несовместимости каждого плагина: для зависимостей без поддержки SPM сохраняется CocoaPods fallback. Подробное описание поведения опубликовано в руководстве Flutter по Swift Package Manager для приложений.

Перед изменением проекта нужно определить, к какой группе он относится:

Сценарий Что проверить до обновления Базовый путь решения
Стандартное приложение Podfile, нативные плагины, Generated-файлы, схема Runner Разрешить автоматическую миграцию, затем сравнить изменения
Старый проект с CocoaPods Источники каждого плагина, lock-файлы, посторонние Pod-скрипты Сохранить CocoaPods fallback и мигрировать по зависимостям
Несколько Flavor или Target Схемы, Build Configuration, скрипты подготовки и пути артефактов Проверять Debug, тесты и Archive отдельно для каждой конфигурации
add-to-app Встраивание Flutter-модуля, ресурсы, зависимости главного iOS-проекта Не объединять старый Framework/Pods-путь с новой схемой без проверки
CI-узел SSH-среда, кэш, сертификаты, экспорт и восстановление после перезапуска Сначала прогнать изолированный узел, затем менять production runner

До миграции следует сохранить pubspec.lock, Podfile, Podfile.lock, каталог ios, настройки схем и конфигураций сборки, файлы подписи, журналы последней успешной сборки и хеш исходного коммита. Также нужен полный список команд CI с переменными окружения. Без этой фиксации команда сможет увидеть, что сборка «сломалась», но не сможет доказать, какая именно часть окружения изменилась.

Нужно ли после обновления Flutter 3.47 для iOS обязательно переходить на Swift Package Manager? Нет, обязательное удаление CocoaPods из любого проекта не следует из изменения значения по умолчанию. Если конкретный плагин ещё не поддерживает SPM, смешанная схема может быть штатным fallback. Решение принимается по фактическому источнику зависимости и результату нативной компиляции, а не по одному наличию Podfile.

02

Безопасная миграция стандартного проекта

Для проекта, близкого к шаблонному, задача состоит не в ручном переписывании Xcode-настроек, а в наблюдении за тем, какие файлы Flutter изменяет сам. Сначала создаётся отдельная ветка и чистый рабочий каталог. Производственный узел при этом не переключается: он должен продолжать собирать исходный коммит по старой схеме.

Последовательность можно организовать так:

  • зафиксировать текущий Flutter SDK, Xcode, Ruby-инструменты и состояние зависимостей;
  • создать изолированный узел или отдельную машину для миграции;
  • обновить Flutter SDK до требуемой стабильной версии и выполнить проверку окружения;
  • получить зависимости проекта без удаления существующих файлов CocoaPods;
  • открыть iOS-проект и сравнить изменения в Xcode-проекте, схемах и скриптах;
  • проверить симулятор, физическое устройство, тестовую сборку и Release-путь;
  • выполнить Archive и экспорт, после чего сравнить полученный артефакт с предыдущим.

Команды должны выполняться в чистом каталоге, а не поверх незакоммиченных изменений:

git status --short
flutter --version
flutter doctor -v
flutter pub get
git diff -- ios pubspec.lock

Эти команды не доказывают готовность релиза сами по себе. Их назначение — зафиксировать исходное состояние и увидеть, какие изменения появились после разрешения зависимостей. Особенно важно отдельно просмотреть ios/Runner.xcodeproj/project.pbxproj, конфигурационные файлы и shared schemes: успешное открытие проекта в Xcode не подтверждает корректность всех вариантов сборки.

Контрольная точка Успешный результат Артефакт, который нужно сохранить
Разрешение зависимостей Нет необъяснимых изменений версий или источников Diff lock-файлов и журнал команды
Swift-пакеты Все ожидаемые пакеты видны в проекте и собираются Xcode-настройки и лог компиляции
Flutter-скрипты Подготовка Flutter-фреймворка выполняется без ручного обхода Полный CI-лог
Симулятор Приложение запускается в тестовой конфигурации Лог запуска и тестов
Физическое устройство Подписанное приложение устанавливается и запускается Лог установки, Bundle ID и профиль
Archive Архив создаётся из нужной схемы Путь к .xcarchive и лог Archive
Экспорт Получается ожидаемый пакет для распространения ExportOptions и журнал экспорта

Поддержка SPM не отменяет необходимости проверять итоговый Xcode-проект. Flutter прямо описывает переходный режим, в котором часть зависимостей может разрешаться через SPM, а часть — через CocoaPods. Поэтому после миграции следует сравнить не только pubspec.lock, но и фактический граф нативных зависимостей.

03

Смешанная схема CocoaPods и SPM

Смешанный проект — это не автоматически неудачная миграция. Проблема появляется тогда, когда команда не знает, какая зависимость подключается каким способом, где формируется её нативный продукт и какая минимальная версия iOS требуется для компиляции. Для каждого плагина нужен отдельный короткий реестр:

Плагин:
Источник:
Способ разрешения:
Нативный продукт:
Минимальная версия iOS:
Debug:
Release:
Archive:
Решение:

Такой реестр нужно заполнить после реального flutter pub get, открытия проекта и сборки. Нельзя объявлять плагин совместимым только потому, что приложение запустилось в Debug: Release может использовать другой флаг, другую схему линковки или иной набор ресурсов.

Может ли Flutter iOS-проект продолжать использовать CocoaPods? Да, если используемая зависимость ещё не поддерживает Swift Package Manager или проект сохраняет проверенную интеграцию, необходимую для текущего релиза. В этом случае CocoaPods следует не «запретить», а контролируемо оставить: сохранить lock-файл, документировать источник каждого Pod и проверить чистую установку на новом узле.

Далее применяется условная развилка:

  • если все критичные плагины разрешаются через SPM, проходят Release и Archive, а diff проекта понятен — можно продолжать миграцию;
  • если один или несколько плагинов работают только через CocoaPods, но сборка и подпись проходят — оставить двойную схему до отдельной замены этих плагинов;
  • если плагин компилируется в Debug, но ломает Archive или экспорт — вернуть рабочую интеграцию и отложить переключение;
  • если после обновления невозможно воспроизвести старую сборку — остановить миграцию и восстановить исходный узел из зафиксированной ветки;
  • если поведение зависит от неподтверждённой жалобы сообщества, не использовать её как основание для решения, пока проблема не воспроизведена на проекте.

Официальное описание перехода к SPM и fallback через CocoaPods находится в материалах Flutter о Swift Package Manager. Конкретные плагины всё равно требуют проверки по их актуальной документации и исходному коду.

04

Многочисленные конфигурации и собственные Target

В проекте с Flavor нельзя считать миграцию завершённой после успешной сборки Runner в Debug. Для каждой конфигурации нужно сопоставить Scheme, Build Configuration, bundle identifier, файл настроек и путь к артефакту. Например, staging может запускаться локально, тогда как production использует отдельную подпись и ломается только во время Archive.

Проверка должна включать:

  • отдельную схему для каждого выпускаемого Flavor;
  • соответствие схемы нужной Build Configuration;
  • вызов скриптов подготовки Flutter для каждой конфигурации;
  • наличие Swift-пакетов в собственных Target, а не только в основном Runner;
  • тесты с теми же переменными, которые применяет CI;
  • Archive и экспорт для каждого реально публикуемого варианта.

Для автоматизации полезно вынести команды в скрипт, но не скрывать ошибки:

set -euo pipefail

flutter pub get
flutter test
xcodebuild \
  -workspace ios/Runner.xcworkspace \
  -scheme "$SCHEME" \
  -configuration "$CONFIGURATION" \
  -destination "generic/platform=iOS" \
  archive \
  -archivePath "$ARCHIVE_PATH"

Имена переменных здесь должны приходить из CI, а не быть зашиты в репозиторий. После выполнения сохраняются схема, конфигурация, commit SHA, лог xcodebuild и путь к архиву. Нельзя принимать единственный успешный Debug-прогон за доказательство готовности production Flavor.

Как проверять SPM в проекте с несколькими Flavor? Сначала нужно выбрать каждую публикуемую схему, затем отдельно выполнить разрешение зависимостей, тесты, Archive и экспорт. Если Swift-пакет добавлен только в основной Target, а staging или production используют собственный Target, результат может различаться. Успешной считается не «общая» сборка, а полный набор проверок для каждого выпускаемого варианта.

05

Интеграция add-to-app

В add-to-app главным объектом миграции является не только Flutter-модуль, но и существующее нативное iOS-приложение. В главном проекте могли остаться Framework-подключения, Pod-интеграция, собственные ресурсы, несколько Target и отдельные правила запуска Flutter-модуля. Добавление новой схемы поверх старой без инвентаризации создаёт риск двойного подключения фреймворков или расхождения ресурсов.

Что проверять после обновления Flutter add-to-app? Сначала нужно определить, каким способом главный проект получает Flutter-модуль, какие пакеты принадлежат нативному приложению, а какие — модулю, и где выполняется его инициализация. Затем проверяются запуск нативного экрана с переходом во Flutter, возврат из Flutter в нативный экран, Release-сборка и восстановление старого способа интеграции.

Официальная инструкция Flutter для add-to-app на iOS должна использоваться как базовая точка сравнения, но старые настройки проекта нельзя удалять механически. Практический план таков:

  • создать копию главного iOS-проекта и Flutter-модуля;
  • выписать старые Framework-, Pod- и пакетные подключения;
  • проверить инициализацию Flutter-модуля из нативного экрана;
  • собрать нативное приложение в Release;
  • проверить ресурсы, bundle identifier и конфигурации;
  • выполнить повторную сборку после очистки Derived Data;
  • провести восстановление старой интеграции из отдельной ветки.

Если старый путь не восстанавливается из коммита и сохранённых lock-файлов, проект ещё не готов к переключению. В этом сценарии разумнее продолжить двойную схему и планировать замену зависимостей отдельно, чем связывать обновление Flutter с одновременной перестройкой всего главного iOS-приложения.

06

Удалённый Mac как изолированный CI-узел

Удалённый Mac полезен не потому, что сам по себе устраняет миграционные риски, а потому, что позволяет отделить новый инструментальный стек от production runner. Для такого узла нужно зафиксировать версии Flutter и Xcode, состояние репозитория, кэш Pub и нативных зависимостей, расположение сертификатов и поведение SSH-сеанса.

Пять обязательных этапов проверки:

  • подключиться по SSH в неинтерактивном режиме и проверить PATH, HOME, доступность Flutter и Xcode;
  • выполнить чистую сборку без заранее подготовленного кэша;
  • повторить сборку с кэшем и сравнить результат;
  • выполнить подпись, Archive и экспорт с теми же секретами, что применяются в CI;
  • перезапустить узел и проверить, что рабочие каталоги, права и инструменты восстанавливаются предсказуемо.

Пример минимальной диагностики:

ssh build-mac 'echo "$PATH"; command -v flutter; xcodebuild -version; git status --short'

Нужно отдельно проверить, что SSH-команда не рассчитывает на открытое графическое окно. Переменные, доступные в пользовательской сессии macOS, могут отсутствовать у non-interactive shell. Это касается путей к Flutter, ключей подписи, HOME, каталогов кэша и секретов CI.

Для подписи следует разделять доступ к сертификатам и профилям, не помещая секретные файлы в репозиторий. Apple описывает управление командными сертификатами в документации по signing identities. Процедуру Archive и экспорт нужно сверять с официальным руководством Apple по распространению приложений и отдельными материалами об Archive и экспорте.

Решение о переключении production принимается только после записи результатов:

Коммит:
Версия Flutter:
Версия Xcode:
Схема:
Чистая сборка:
Сборка с кэшем:
Физическое устройство:
Archive:
Экспорт:
Перезапуск узла:
Результат:
Путь отката:

Для SSH-ориентированной работы полезно заранее проверить руководство NodeMini по удалённому Mac, а параметры доступа и восстановления сверить с центром помощи NodeMini. Это не заменяет проверку проекта: её следует проводить на реальном Flutter-репозитории, а не на демонстрационном приложении.

07

Условия переключения и отката

Главный production-узел можно менять только при одновременном выполнении всех условий:

  • изменения в Xcode-проекте и lock-файлах просмотрены и объяснены;
  • для каждого критичного плагина известен источник зависимости;
  • все выпускаемые Flavor и Target прошли тесты и Archive;
  • физическое устройство принимает подписанную сборку;
  • экспорт создаёт ожидаемый пакет;
  • CI работает в SSH-сеансе без ручного открытия Xcode;
  • чистая и кэшированная сборки дают воспроизводимый результат;
  • старый коммит и старый узел можно восстановить без ручного поиска файлов.

Если хотя бы одно условие не выполнено, выбирается один из безопасных вариантов:

  • оставить production на старой версии, а новый удалённый Mac использовать для диагностики;
  • сохранить CocoaPods fallback для проблемных плагинов;
  • заменить несовместимый плагин отдельной задачей;
  • перенести переключение на следующий релизный цикл;
  • полностью вернуть ветку и конфигурацию к последнему подтверждённому состоянию.

Собственный запасной Mac часто выглядит проще, но для такой миграции он создаёт дополнительные расходы и ограничения: устройство занято локальными задачами, его сложнее изолировать от разработчика, а восстановление после сбоя зависит от физического доступа; кроме того, домашняя сеть, сон macOS и ручное обновление сертификатов могут нарушить ночной CI. Для постоянной тяжёлой нагрузки и физических USB-устройств локальный Mac всё ещё может быть рациональнее, но для временного миграционного стенда он не всегда оправдан.

Если в команде нет свободного Mac для изоляции, аренда NodeMini позволяет на один релизный цикл воспроизвести Flutter iOS-конвейер на отдельном узле с доступом через SSH, VNC или веб-консоль и с правами root. Такой вариант не отменяет проверок и не делает CocoaPods или SPM совместимыми автоматически, зато отделяет эксперимент от production и даёт понятную точку освобождения узла после завершения миграции. При необходимости параметры доступных сценариев можно уточнить через русскоязычный раздел NodeMini.

Для команды, которая уже использует домашний Mac mini, текущая схема обычно имеет три слабых места: ограниченный физический доступ к машине, зависимость CI от локального питания и сети, а также риск непреднамеренно изменить рабочее окружение во время проверки. Аренда NodeMini даёт более управляемый временный контур для миграции, поэтому сначала стоит арендовать независимый Mac на один выпуск, пройти весь список, а затем решить — сохранять его как постоянный CI-узел, расширять схему или отказаться от него после успешного переключения.