Сегодня мы поговорим о том, как активировать режим Swift 6 в ваших Xcode-проектах и SwiftPM-модулях, и каким может быть опыт миграции на новую версию языка.
Что такое «режим Swift 6»?
К сожалению, как нам пояснил Дуг Грегор (Doug Gregor) из Swift Language Workgroup, Swift 6 все-таки не успеет увидеть свет в 2023 году. Но знали ли вы, что Apple уже зарелизила некоторые части Swift 6 в версии 5.8? Да, вы не ослышались. Эти части нового Swift поставляются с Xcode 14.3, но они по умолчанию отключены. Они заработают сразу после выхода Swift 6, что может занять еще один год или даже больше.
Эти фичи вносят ряд критических изменения в Swift, в основном из-за переименования общеизвестных API, корректировки их поведения и добавления новых проверок безопасности в компилятор. Все это в какой-то момент будет активировано с целью помочь улучшить нашу кодовую базу. И нам обязательно придется обновлять наши проекты, чтобы они не поломались из-за этих изменений.
Я решил, что было бы неплохо включить все фичи в моих текущих проектах уже сейчас, чтобы увидеть, есть ли в моей кодовой базе какие-либо критические изменения, о которых мне следует позаботиться. И, конечно же, таким образом я бы смог попробовать некоторые новые фичи, такие как BareSlashRegexLiterals
, что дает нам возможность краткой инициализации регулярных выражений с помощью литерального синтаксиса (/.../
).
К счастью, в Swift 5.8 есть легкий универсальный способ активировать все эти опции: нам просто нужно передать в Swift флаг -enable-upcoming-feature
, прописав его в OTHER_SWIFT_FLAGS
настройках сборки нашего проекта в Xcode. А также нам нужно знать, какие именно фичи нам доступны, но я не смог найти хорошего комплексного обзором (по крайней мер пока). Предложение, в рамках которого был добавлен этот унифицированный способ активации, содержит такой список, но он не обновляется более новыми опциями, добавленными позже, как, например, в SE-0384. Так где же мы можем получить достоверный актуальный список всех поддерживаемых опций?
✨ АПДЕЙТ: теперь вы можете найти эту информацию прямо на Swift.org, просто указав в фильтре флаг “upcoming”.
Swift имеет открытый исходный код, поэтому более авторитетный источник, чем репозиторий Swift на GitHub, трудно себе представить! В нем есть файл Features.def
, содержащий строки (ссылка на ветку release/5.8
) с UPCOMING_FEATURE
, которые включаю соответствующий номер предложения Swift Evolution и версию Swift, которой они будут активны:
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
UPCOMING_FEATURE(ExistentialAny, 335, 6)
Ниже представлено краткое описание того, что эти опции делают:
ConciseMagicFile:
Делает суть директивы#file
ближе к#fileID
нежели#filePath
.ForwardTrailingClosures:
Меняет правило сканирования замыканий с конца на правило сканирования с начала.ExistentialAny:
Делает использование ключевого слова any для экзистенциальных типов обязательным.BareSlashRegexLiterals:
Добавляет литеральный синтаксис регулярных выражений (/.../
).
В этом списке почему-то не хватает еще двух опций (я пытаюсь выяснить почему):
StrictConcurrency:
Добавляет полную проверку параллельного кода.Implicit Open Existentials:
Добавляет выполнение неявного открытия для ряда дополнительных сценариев.
В более поздних версиях мы увидим еще больше опций (например, ImportObjcForwardDeclarations).
Ко всему вышеперечисленному я также решил добавить -warn-concurrency
(на самом деле это должно быть приблизительно то же, что и StrictConcurrency, если эта опция уже полностью реализована) и -enable-actor-data-race-checks
, чтобы протестировать дополненную поддержку параллелизма на своем коде.
Как мой проект пережил миграцию
Если вам тоже интересно попробовать включить в своем проекте все опции, доступные в версии 5.8, то вы можете просто скопировать (⌘C) следующий кусок текста, затем перейдите на вкладку "Build Settings" вашего проекта Xcode, найдите "Other Swift Flags", выберите этот параметр в редакторе и вставьте (⌘V) его туда:
//:configuration = Debug
OTHER_SWIFT_FLAGS = -enable-upcoming-feature BareSlashRegexLiterals -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures -enable-upcoming-feature ImplicitOpenExistentials -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks
//:configuration = Release
OTHER_SWIFT_FLAGS = -enable-upcoming-feature BareSlashRegexLiterals -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures -enable-upcoming-feature ImplicitOpenExistentials -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks
//:completeSettings = some
OTHER_SWIFT_FLAGS
Если вы используете модульное приложение на SwiftPM, как и я, или если вы работаете над пакетом для Swift, вам нужно будет дополнительно передавать массив .enableUpcomingFeature
для каждого таргета в swiftSettings
:
let swiftSettings: [SwiftSetting] = [
.enableUpcomingFeature("BareSlashRegexLiterals"),
.enableUpcomingFeature("ConciseMagicFile"),
.enableUpcomingFeature("ExistentialAny"),
.enableUpcomingFeature("ForwardTrailingClosures"),
.enableUpcomingFeature("ImplicitOpenExistentials"),
.enableUpcomingFeature("StrictConcurrency"),
.unsafeFlags(["-warn-concurrency", "-enable-actor-data-race-checks"]),
]
let package = Package(
// ...
targets: [
// ...
.target(
name: "MyTarget",
dependencies: [/* ... */],
swiftSettings: swiftSettings
),
// ...
]
Не забудьте обновить версию инструментов в верхней части файла до 5.8
:
// swift-tools-version:5.8
Вам нужно будет следить за этим, так как Swift не передает параметры, указанные вами для таргетов вашего проекта, автоматически в каждый модуль, который вы импортируете в свой проект. И это на самом деле хорошо, потому что таким образом пакеты Swift, которые вы подключаете к своему проекту, не нуждаются в каких-либо корректировках, а вы можете использовать эти фичи только для кода вашего собственного приложения. И наоборот: авторы пакетов могут активировать эти фичи в рамках своих проектов, не затрагивая код в проектах, в которых они используются.
После их активации и запуска сборки я столкнулся с четырьмя видами проблем:
1. Мне пришлось добавить в нескольких местах ключевое слово any
— Xcode помог мне с этим:
2. Почему-то я получил много ошибок для вью с модификатором .sheet
. Это были пары ошибок "Generic parameter 'Content' could not be inferred" («Не возможно вывести тип обобщенного параметра 'Content'») и "Missing argument for parameter 'content' in call" («В вызове недостает аргумента для параметра 'content'»). Однако эти сообщения оказались не очень информативными, поэтому я сначала попытался заменить содержимое .sheet
на простенький Text
, но это не помогло. Поэтому, я стал пробовать отключать новые опции одну за другой, и ошибки исчезли, когда я выключил ForwardTrailingClosures
. По итогам я так и оставил ее выключенной. Я надеюсь, что в будущих версиях Swift появятся более информативные сообщения об ошибках, которые помогут разобраться с этим изменением. А пока спешить некуда. Я могу повторить попытку на Swift 5.9. Сейчас же у меня не было времени разбираться с этим.
3. Мне пришлось пометить некоторые из моих функций, возвращающих ТСА WithViewStore
, атрибутом @MainActor
, но опять же, Xcode помог мне с этим.
4. Во многих местах всплывали предупреждения “Non-sendable type ‘…’ passed in call to main actor-isolated function cannot cross actor boundary” («тип ‘…’, не являющийся sendable, переданный в вызове основной функции, изолированной в глобальным актором, не может выходить за границу актора»). Поэтому мне пришлось сделать эти типы соответствующими протоколу Sendable
(узнать о Sendable
больше можно здесь).
Все остальные ~ 35 тысяч строк кода Swift моего приложения. RemafoX, казалось, собирались без каких-либо проблем. Весь процесс миграции занял не более 3 часов моего времени.
На данном этапе мой проект почти готов к новому Swift ????, и теперь я могу использовать новую литеральную форму Regex (let regex = /.*@.*/
). Кроме того, теперь я не смогу написать новый код, который мне впоследствии нужно будет переписывать под Swift 6, потому что я сразу же получу ошибки. ????
А что насчет ваших проектов? Какие будущие фич вы ждете больше всего?
Перевод статьи подготовлен в преддверии старта курса iOS Developer. Professional. Также приглашаю на бесплатный вебинар в рамках которого поговорим о технологии Deep links на iOS, произведем настройку сервера, клиентского приложения для работы с технологией. Напишем простой пример, как открыть определенный экран с помощью Deep links на примере SwiftUI - приложения.