Сначала зафиксируйте текущую сборку и переводите на Swift 6 language mode только отдельные Target; немедленно переключать весь проект не нужно. Такой подход подходит командам, которые обновляют компилятор Swift 6.3, но хотят сохранить стабильные релизы, пока проверяются конкурентность, зависимости, смешанный код и CI.
Кому нужен этот план
Эта инструкция предназначена разработчикам iOS и macOS, у которых после включения проверок Swift Concurrency появились многочисленные диагностические сообщения.
Она также пригодится техническим руководителям проектов с несколькими модулями, Objective-C или C-интерфейсами, старыми пакетами и двоичными зависимостями. Инженеры сборки найдут здесь схему параллельной проверки двух режимов в CI.
По состоянию на 24 августа 2026 года Swift 6.3 официально выпущен; сведения о выпуске и изменениях сверяйте с официальным объявлением Swift 6.3. Конкретные ошибки миграции зависят не только от версии компилятора, но и от настроек Target, структуры кода и версий зависимостей.
Ошибки миграции Swift 6.3: исходная точка
Главная причина хаотичной миграции — смешение двух понятий. Компилятор Swift 6.3 может использоваться для сборки проекта, в котором разные Target по-прежнему работают в разных режимах языка. Swift language mode определяет правила анализа конкретной цели, а версия компилятора определяет доступный набор инструментов и синтаксических возможностей.
Из этого следуют несколько важных ограничений:
- переключение проекта на новый компилятор само по себе не показывает полный объём проверок Swift 6;
- изменение глобальной настройки может затронуть приложение, тесты, утилиты, расширения и пакетные модули одновременно;
- старый пакет иногда собирается в прежнем режиме, но ломается на границе Sendable или Actor;
- двоичный фреймворк может успешно находиться линкером, однако не пройти проверку интерфейса, подписи или архитектуры;
- сообщения компилятора не равны подтверждённым сбоям во время выполнения: часть диагностики указывает на потенциальную гонку, которую нужно исследовать в контексте доступа к состоянию.
Перед изменениями сохраните снимок текущего состояния. Запишите режим языка для каждого Target, версию инструментария, параметры строгой проверки конкурентности, lock-файл зависимостей, список предупреждений и результат тестовой базы. Число сообщений в этом журнале нужно рассматривать как исходную метрику конкретного проекта, а не как универсальный показатель сложности Swift 6.3.
Проверьте настройки не только основного приложения. Отдельно просмотрите тестовые цели, командные утилиты, расширения, демонстрационные приложения и внутренние пакеты. Частая ошибка — перевести библиотечный Target, но оставить его тесты или потребителя в другом режиме, а затем считать возникший конфликт проблемой компилятора.
Руководство Macstripe по конфигурации заказа полезно использовать как организационную часть подготовки отдельного Mac-окружения: до миграции определите, какой узел отвечает за стабильную ветку, а какой — за экспериментальную.
Карта диагностик конкурентности
Не начинайте с добавления @unchecked Sendable, отключения предупреждений или широкого nonisolated. Такие решения могут быстро уменьшить видимое число сообщений, но одновременно скрыть реальную гонку и перенести ошибку в рабочее выполнение.
Разложите диагностику по четырём категориям.
Общее изменяемое состояние. Найдите глобальные переменные, одиночные объекты, кэши, делегаты и коллекции, к которым обращаются из разных асинхронных контекстов. Для каждого объекта зафиксируйте владельца, допусточный поток или Actor и операции записи. Если владельца нет, сначала спроектируйте его, а не добавляйте атрибут, разрешающий небезопасную передачу.
Actor-изоляция. Когда состояние действительно должно принадлежать одному последовательному контексту, Actor может быть правильной границей. Однако перенос метода внутрь Actor меняет способ вызова: вызывающий код может получить асинхронную границу и необходимость явного await. Проверьте цепочку вызовов до пользовательского интерфейса, фоновой задачи и тестов. Описание модели Actor приведено в документации Apple об Actor.
Sendable. Тип, передаваемый между конкурентными контекстами, должен быть безопасен для такого обмена. Значимый неизменяемый тип обычно проще проверить, чем объект с внутренним изменяемым состоянием. Для классов и обёрток не ограничивайтесь соответствием протоколу: проверьте, кто меняет свойства, где создаются экземпляры и не передаётся ли ссылка через замыкание.
Асинхронные границы. Особое внимание уделите callback API, делегатам, очередям, таймерам и мостам между Objective-C и Swift. Именно здесь компилятор часто обнаруживает несоответствие между старой моделью «вызвать позже» и новой моделью изоляции. Сопоставьте каждую границу с фактическим исполнителем, а затем добавьте тест, который проверяет порядок, отмену и повторный вызов.
Для последовательной работы используйте официальное руководство по постепенной миграции конкурентного кода. В нём важен сам принцип постепенного включения проверок: сначала ограничивается область изменений, затем исправляются границы, и только после этого повышается строгость для следующего Target.
Сценарий из проекта
Представьте приложение с модулем синхронизации, экранным Target и тестовой целью. После смены режима диагностика указывает на объект состояния, который обновляется из сетевого callback и читается экраном. Если объявить объект «безопасным» без проверки владельца, сборка может пройти, но порядок записи останется неопределённым. Более надёжная последовательность — определить Actor-владельца, адаптировать API синхронизации, обновить тесты и только затем перевести зависимый экранный модуль.
Преимущества такого исправления:
- причина проблемы остаётся видимой в архитектуре;
- граница проверяется отдельно от пользовательского интерфейса;
- дальнейшие Target получают уже более ясный контракт;
- откат ограничивается модулем, а не всем приложением.
Недостатки тоже нужно учитывать:
- публичные методы могут стать асинхронными;
- тестовые фикстуры потребуют явного управления контекстом;
- старый Objective-C API иногда нельзя безопасно описать одним объявлением;
- временно придётся поддерживать разные режимы языка.
Зависимости и двоичные границы
Проблемы сторонних пакетов часто выглядят как ошибки Swift 6.3, хотя фактическая причина находится в зависимости. До исправления исходников проверьте официальный репозиторий каждого проблемного проекта:
- есть ли версия, рассчитанная на новый компилятор;
- опубликовано ли исправление для Sendable, Actor или асинхронных API;
- поставляется ли пакет исходным кодом или готовым бинарным модулем;
- зафиксирована ли версия в lock-файле;
- поддерживается ли нужная комбинация платформ, архитектур и настроек сборки.
Для Swift важно различать совместимость исходников, модулей и готовых артефактов. Общие правила версий описаны в документации Swift о совместимости. Не переносите временное исправление из внешнего пакета во все модули приложения: оберните зависимость адаптером, чтобы потенциально небезопасный контракт имел одну точку контроля.
| Тип зависимости | Что проверить до миграции | Безопасная тактика |
|---|---|---|
| Исходный пакет | Версию, ветку исправления и настройки языка | Обновить и зафиксировать версию, затем прогнать тесты потребителя |
| Бинарный модуль | Совместимость интерфейса, платформы, архитектуры и подписи | Изолировать в отдельном Target и не менять приложение ради одного артефакта |
| Objective-C библиотека | Аннотации nullability, блоки, делегаты и потоковую модель | Создать тонкий Swift-адаптер с документированной границей |
| Внутренний пакет | Публичные типы, Sendable-контракты и зависимые тесты | Перевести сначала пакет с наименьшим графом потребителей |
Если зависимость пока не поддерживает Swift 6.3, у команды есть несколько вариантов: временно оставить потребляющий Target в прежнем режиме, заменить пакет, дождаться официального исправления или поддержать локальную ветку с чётким сроком пересмотра. Последний вариант оправдан только при наличии тестов и владельца; бессрочная локальная копия создаёт скрытую стоимость сопровождения.
Target и смешанный код
Большой проект нельзя надёжно мигрировать переключением одной общей галочки. Сначала постройте карту: какие Target компилируются первыми, какие экспортируют публичные типы, какие зависят от двоичных модулей и где находятся смешанные интерфейсы Swift, Objective-C и C.
Начните с цели, у которой одновременно:
- мало потребителей;
- есть автоматические тесты;
- нет критичного бинарного внешнего контракта;
- понятен владелец состояния;
- можно быстро вернуть прежний режим.
Затем двигайтесь от нижних библиотечных слоёв к потребителям. Если сделать наоборот, ошибки границ будут смешаны с ошибками экранного или прикладного кода, и вы потеряете причинную связь.
| Участок проекта | Основной риск | Критерий перехода дальше |
|---|---|---|
| Независимый Swift-пакет | Неполный Sendable-контракт | Сборка и тесты в новом режиме проходят |
| Общая библиотека | Изменение публичных async-методов | Все потребители компилируются в проверочной ветке |
| Objective-C/Swift граница | Неясная модель исполнителя callback | Есть тесты на поток, отмену и повторный вызов |
| Основной продуктовый Target | Смешение архитектурных и инфраструктурных ошибок | Диагностика классифицирована, а регрессии воспроизводимы |
| Расширения и тесты | Забытые настройки и другой жизненный цикл | Все целевые среды проверены тем же сценарием |
На границе с Objective-C проверьте не только объявления. Нужно понять, может ли callback прийти одновременно, кто владеет передаваемым объектом и допускается ли вызов после отмены. Для C-интерфейса отдельно проверьте указатели, время жизни памяти и потоковую безопасность: соответствие типа Swift не делает внешний API безопасным автоматически.
Параллельный CI и откат
Миграционная ветка не должна ломать текущую публикацию. Сохраните стабильный job со старым языковым режимом и добавьте отдельный job для Target, который переводится первым. В обеих ветках сравнивайте не только успешность компиляции, но и тесты, упаковку, подпись, запуск на целевой платформе и поведение создаваемого артефакта.
Для macOS-поставки подпись нельзя считать второстепенной проверкой: сверяйте процедуру с официальным описанием создания подписанного кода для распространения. Обновление инструментария может совпасть с изменением сертификатов, профилей или цепочки упаковки, поэтому результаты компиляции и доставки храните раздельно.
Рекомендуемая последовательность выглядит так:
- создайте отдельную миграционную ветку и зафиксируйте исходный lock-файл;
- сохраните параметры компилятора и режим языка каждого Target;
- выберите один малый Target с воспроизводимыми тестами;
- включите нужную строгость только в его проверочном job;
- классифицируйте диагностику по состоянию, Actor, Sendable и асинхронным границам;
- исправьте контракты и добавьте регрессионные тесты;
- обновите зависимость или изолируйте её адаптером;
- проверьте потребителей, подпись и создаваемый артефакт;
- перенесите следующий Target только после прохождения предыдущего набора проверок;
- сохраните рабочий job старого режима до завершения миграции всех целевых сред.
Параллельная схема даёт две выгоды: команда продолжает выпускать стабильную ветку, а миграция получает реальные результаты CI вместо локального эксперимента. Цена — дополнительные узлы, время сопровождения настроек и необходимость не допустить расхождения между job. Поэтому обе цепочки должны использовать одинаковые версии зависимостей и сравнимые сценарии тестирования.
Условия завершения
Миграцию нельзя объявлять готовой только потому, что основной Target собирается. Зафиксируйте критерии до начала работ:
- [ ] Для каждого Target записан выбранный Swift language mode и причина его текущего состояния.
- [ ] Все критичные диагностики конкурентности классифицированы, а не просто подавлены.
- [ ] Общие изменяемые объекты имеют подтверждённого владельца или изолированную границу.
- [ ] Для Actor, Sendable и асинхронных callback добавлены регрессионные тесты.
- [ ] Версии исходных и двоичных зависимостей зафиксированы и проверены в чистой сборке.
- [ ] Swift, Objective-C и C-интерфейсы протестированы на фактических границах вызова.
- [ ] Стабильный и миграционный CI jobs выполняют сборку, тесты и проверку артефакта.
- [ ] Подпись, упаковка и запуск на всех целевых средах подтверждены отдельно от компиляции.
- [ ] Откат проверен: команда умеет вернуть режим языка, зависимости и публикуемый узел.
- [ ] Старый инструментальный job удаляется только после согласования владельцами продукта и сборки.
Тесты конкурентности должны проверять не только «нет ли падения». Добавьте сценарии отмены задачи, повторного входа, одновременного чтения и записи, завершения callback после закрытия объекта и повторного запуска синхронизации. Если проблема обнаруживается только на CI, сохраните минимальный воспроизводимый тест рядом с модулем, а не в виде устной инструкции для дежурной смены.
Частые вопросы миграции
После обновления до Swift 6.3 появилось слишком много ошибок конкурентности. Что делать?
Не исправляйте сообщения по одному без классификации. Сначала определите Target и язык режима, затем отделите общий изменяемый стейт от Actor-изоляции, Sendable и асинхронных границ. Приоритетом должны быть реальные пути совместного доступа и тесты, а не уменьшение счётчика предупреждений через небезопасные объявления.
Чем отличаются компилятор Swift 6 и Swift language mode?
Компилятор — это версия инструментария, а режим языка — правила, применяемые к конкретному Target. Новый компилятор может собирать старый режим языка, поэтому нельзя делать вывод о полном переходе по одной версии среды. Проверяйте настройки приложения, тестов, расширений и пакетов отдельно.
Как перевести большой проект на Swift 6 по Target?
Выберите изолированную цель с небольшой областью потребителей и полным набором тестов. Переведите её, исправьте публичные контракты и проверьте зависимые Target. Затем повторяйте процедуру для следующего слоя. Основную сборку не переводите до тех пор, пока промежуточная ветка не проходит компиляцию, тестирование и проверку артефакта.
Как поступить с зависимостью, которая ещё не поддерживает Swift 6.3?
Проверьте официальный репозиторий, совместимую версию и опубликованные исправления. До обновления можно оставить один потребляющий Target в прежнем режиме или спрятать пакет за адаптером. Не распространяйте @unchecked Sendable по приложению только ради внешней зависимости и не считайте локальную правку бинарного модуля полноценным решением.
Как откатить неудачную миграцию Swift 6.3?
Верните согласованный набор: исходники, режим языка, lock-файл, параметры сборки и CI job. Публикация должна оставаться привязанной к стабильной ветке, пока миграционная не проходит тесты, подпись и упаковку. После отката сохраните минимальный воспроизводимый случай, иначе следующая попытка повторит ту же ошибку.
Последняя проверка перед переключением
Перед объединением миграционной ветки выполните [ ]-проверку на чистом узле, а не только на рабочем компьютере разработчика. Убедитесь, что зависимости устанавливаются из зафиксированных источников, промежуточные артефакты не маскируют проблему, а настройки Target не берутся случайно из локального файла.
Для каждой найденной диагностики задайте три вопроса: какое состояние пересекает границу, кто им владеет и какой тест доказывает безопасное поведение. Если ответа нет, изменение ещё не готово к объединению. Если исправление требует небезопасной аннотации, оформите её как исключение с владельцем, объяснением и тестом; не используйте её как универсальный способ очистить журнал компиляции.
Компилятор Swift 6.3 следует рассматривать как инструмент постепенного перехода, а не как команду одномоментно переписать архитектуру. Сверяйте решения с официальным руководством по миграции Swift и материалами Apple о конкурентном коде, но применяйте их к фактическим границам вашего проекта.
Если сейчас вы собираете проект на единственном Mac-узле, постоянная миграция на нём создаёт три конкретных риска: эксперимент может занять очередь релизной сборки, локальные настройки смешаются со стабильным окружением, а неудачный откат затронет подпись и доставку. Для краткого теста отдельный Mac обычно практичнее покупки ещё одной машины, особенно когда нужно проверить лишь несколько Target. В таком сценарии аренда Mac для изолированной сборки от Macstripe позволяет вынести миграционную ветку на отдельное окружение, сохранив текущий узел для выпуска.
Перед выбором проверьте требования к доступу, сроку работы и физическим интерфейсам: для постоянной тяжёлой нагрузки, локальных устройств или длительного стабильного конвейера собственный Mac может оказаться рациональнее. Если же вам нужны временный CI-узел, воспроизводимая проверка Swift 6.3 и возможность отката без остановки релизов, начните с контакта с Macstripe, описав Target, зависимости и сценарий проверки.