Подготовка моего приложения к Swift 6

28 апреля 2023

Как включить режим Swift 6 для ваших Xcode-проектов и SwiftPM-модулей уже сегодня. А также каков опыт миграции.

Автор: Джихат Гюндюз

Дата публикации: 19 апреля 2023 г.
Время чтения: 5 минут

Preparing My App for Swift 6


Что такое "режим Swift 6”?

Swift 6 не будет выпущен в 2023 году, это было ясно сказано Дагом Грегором из Рабочей группы по Swift Language. Но вы знали, что Apple уже добавила элементы Swift 6 в 5.8? Да, это правда. Некоторые компоненты Swift, внесенные вместе с Xcode 14.3 несколько недель назад, по умолчанию выключены. Они будут включены, как только будет выпущен Swift 6, что может занять еще год или больше.

Эти элементы вносят некоторые изменения в Swift, например, переименовывают известные API, корректируют их поведение или добавляют в компилятор новые проверки безопасности. Но в какой-то момент все они будут включены, чтобы улучшить наши кодовые базы. И нам придется обновлять наши проекты, чтобы соответствовать этим изменениям.

Я подумал: хорошая идея - включить все функции в моих проектах, чтобы узнать, есть ли какие-то критические изменения кодовой базы, о которых я должен знать. И, конечно, я также мог бы поюзать некоторые новые функции. Одна из них - BareSlashRegexLiterals, делающая синтаксис литерала регулярных выражений /.../ доступным для краткой инициализации Regex.

К счастью, с Swift 5.8 теперь есть единый способ включения этих параметров: мы просто должны передать -enable-upcoming-feature в Swift, указав его в OTHER_SWIFT_FLAGS в настройках сборки нашего проекта в Xcode. Но нам также нужно знать, какие из функций доступны, а я не смог найти ничего с верным описанием (пока что). Рекомендация, вводящая данный единый параметр, содержит вот такой список, но в дальнейшем он не был обновлен с позже добавленными новыми параметрами, например, из SE-0384. Итак, где сейчас мы можем получить точный список всех поддерживаемых параметров?

✨ ОБНОВЛЕНИЕ: Теперь вы можете фильтровать по предстоящим флагам непосредственно на Swift.org.


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: Выполнение полной проверки параллелизма.
  • ImplicitOpenExistentials: Выполняет неявное открытие в дополнительных случаях.

Больше опций будет добавлено в более поздних версиях, например, 
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

.enableUpcomingFeature доступен только в Swift 5.8.

Все потому, что Swift не передает опции, указанные для целевой платформы вашего проекта, автоматически любым модулям, которые вы импортируете в свой проект. Это хорошая новость, т.к. пакеты Swift, которые вы можете включать в свой проект, не требуют каких-либо настроек, и вы все еще можете использовать данные элементы для своего собственного приложения. И наоборот: авторы пакетов могут включить эти элементы для своих проектов, не затрагивая код в проектах, в которые они будут включены.

После включения этих опций и построения проекта я столкнулся с четырьмя типами проблем:

1.    Мне пришлось добавить ключевое слово any в нескольких местах - Xcode помог с исправлением с помощью Fix-It:

2.    Я столкнулся с большим количеством ошибок для вью с модификатором .sheet. Указанные пары ошибок: "Не удалось определить универсальный параметр 'Content'" и "Отсутствует аргумент для параметра 'content' в вызове". Однако эти сообщения были не очень полезными, поэтому я сначала попытался заменить содержимое .sheet на просто Text-вью, но это не помогло. Поэтому я попробовал выключить опции одну за другой, и ошибки исчезли, когда я выключил ForwardTrailingClosures, и я оставил ее выключенной. Я надеюсь, что в будущих версиях Swift появятся лучшие сообщения об ошибках, чтобы в последствии их можно было исправлять. В конце концов спешить некуда. Я могу попробовать на Swift 5.9. Не было времени изучить вопрос.

3.    Мне пришлось пометить свои некоторые функции, возвращающие TCA WithViewStore, атрибутом @MainActor, но здесь снова мне помог Xcode с Fix-It.

4.    Во многих местах были показаны предупреждения: “Не отправляемый тип '...' передан в вызов основной, изолированной актором функции, не может пересечь границу актора". Поэтому я подвел эти типы под протокол Sendable (узнать о Sendable можно здесь).

Все остальное выглядело для моего приложения RemafoX с ~35 тысячами строк Swift-кода отлично работающим. Весь процесс занял менее 3 часов моего времени.

Мой проект теперь готов к будущему Swift 🎉, и я могу использовать новую функцию Регулярных выражений (let regex =  /.@./). Кроме того, я не могу вставить новый код, который я должен буду позже перенести на Swift 6, т.к. сразу же получу ошибки. 💯

Что насчет вашего проекта? К каким будущим функциям вы хотите перейти?
 

Оригинал статьи

Содержание