コンテンツへスキップ

Swift 6に向けたアプリの準備

XcodeプロジェクトとSwiftPMモジュールでSwift 6モードを今すぐ有効にする方法と、移行体験の実際を紹介します。

Swift 6に向けたアプリの準備

「Swift 6モード」とは?

Swift 6は2023年にはリリースされません。それはSwift Language WorkgroupのDoug Gregorが明言しています。しかし、AppleがすでにSwift 6の一部をSwift 5.8に同梱していることをご存知でしたか?そうなんです。数週間前にXcode 14.3とともにリリースされたSwiftの一部の機能はデフォルトでオフになっています。Swift 6がリリースされると有効になりますが、それはまだ1年以上先かもしれません。

これらの機能は、既知のAPIのリネーム、動作の調整、コンパイラへの新しい安全性チェックの追加など、Swiftに破壊的変更をもたらします。しかし、いずれはすべて有効になり、コードベースの改善に役立ちます。そして、これらの変更に対応してプロジェクトを更新する必要があります。

すべての機能をプロジェクトで有効にして、コードベースにとって把握すべき破壊的変更がないか確認するのは良いアイデアだと考えました。もちろん、BareSlashRegexLiteralsのように、簡潔なRegex初期化のための/.../正規表現リテラル構文が使えるようになるなど、新機能を活用することもできます。

ありがたいことに、Swift 5.8ではこれらのオプションを有効にする統一的な方法が用意されています。Xcodeのプロジェクトビルド設定にあるOTHER_SWIFT_FLAGS-enable-upcoming-featureをSwiftに渡すだけです。ただし、利用可能な機能が何かも知る必要がありますが、良い一覧が見つかりませんでした(まだ)。この統一オプションを導入したプロポーザルにはリストが含まれていますが、後から追加される新しいオプション(SE-0384のものなど)では更新されません。では、サポートされているすべてのオプションの信頼できるリストはどこで入手できるのでしょうか?

UPDATE:Swift.orgで直接upcoming flagsでフィルタできるようになりました。

Swiftはオープンソースなので、最も信頼できる場所はSwiftのGitHubリポジトリです!Features.defファイルに、関連するSwift Evolutionプロポーザル番号と有効化されるSwiftバージョンの両方を含むUPCOMING_FEATUREという名前のエントリが含まれています(release/5.8ブランチへのリンク):

UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
UPCOMING_FEATURE(ExistentialAny, 335, 6)

各オプションの簡単な説明は以下の通りです:

なぜかリストに記載されていない2つのオプションがあるようです(調査中):

後のバージョンで追加されるオプションもあります(例:ImportObjcForwardDeclarations)。

適切なConcurrencyサポートのためにコードをチェックするため、-warn-concurrency(実際に動作するならStrictConcurrencyと同等のはず)と-enable-actor-data-race-checksも追加で渡すことにしました。

プロジェクトの移行

私と同じように5.8のすべてのオプションを有効にしたい場合は、以下のテキストブロックをコピー(Cmd+C)してから、Xcodeプロジェクトの「Build Settings」タブで「Other Swift Flags」を検索し、そのオプションをエディタで選択してペースト(Cmd+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

.enableUpcomingFeature is only available from Swift 5.8

私のようにSwiftPMでモジュール化されたアプリを使っている場合や、Swiftパッケージで作業している場合は、各ターゲットのswiftSettings.enableUpcomingFeatureの配列を追加する必要があります:

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
      ),
      // ...
   ]

ファイル先頭のtools versionを5.8にアップグレードするのも忘れないでください:

// swift-tools-version:5.8

.enableUpcomingFeatureはSwift 5.8以降でのみ利用可能

これは、プロジェクトターゲットで指定したオプションをSwiftがインポートしたモジュールに自動的に渡さないためです。これは実は良いニュースです。プロジェクトに含めているSwiftパッケージの調整が不要で、自分のアプリコードだけでこれらの機能を使えるからです。逆もまた然りで、パッケージ作者がプロジェクトでこれらの機能を有効にしても、そのパッケージを利用しているプロジェクトのコードには影響しません。

これらを有効にしてビルドしたところ、4種類の問題に遭遇しました:

  1. 複数の箇所でanyキーワードを追加する必要がありました。XcodeがFix-Itで支援してくれました:

Migrating my project

Migrating my project 2

  1. なぜか.sheetモディファイアを持つビューで大量のエラーが発生しました。エラーペアには「Generic parameter ‘Content’ could not be inferred」と「Missing argument for parameter ‘content’ in call」と表示されましたが、あまり参考になりませんでした。まず.sheetのコンテンツを単なるTextビューに置き換えてみましたが解決しませんでした。そこでオプションを1つずつオフにしていったところ、ForwardTrailingClosuresをオフにした時にエラーが消えたため、このオプションはオフのままにしました。将来のSwiftバージョンでより良いエラーメッセージが生成されることを期待して、その時に修正するつもりです。急ぐ必要はありません。Swift 5.9で再試行できます。今は調査する時間がありませんでした。

Migrating my project 3

  1. TCAWithViewStoreを返す一部の関数に@MainActorアトリビュートを付ける必要がありましたが、ここでもXcodeがFix-Itで支援してくれました。

Migrating my project 4

  1. 多くの箇所で「Non-sendable type ‘…’ passed in call to main actor-isolated function cannot cross actor boundary」という警告が表示されました。そのため、これらの型をSendableプロトコルに準拠させました(Sendableについてこちらで学べます)。

自分のアプリRemafoX(約35,000行のSwiftコード)では、それ以外は問題なくビルドできました。プロセス全体にかかった時間は3時間未満でした。

これでプロジェクトはSwiftの未来に備えられました。新しいRegexリテラル機能(let regex = /.*@.*/)も使えます。また、後でSwift 6に移行する必要のある新しいコードを書いてしまうこともありません。すぐにエラーが出るからです。

あなたのプロジェクトはいかがですか?どのupcoming featureに移行したいですか?

この記事が参考になりましたか?BlueskyMastodonでフォローして、Swiftのヒントやインディー開発の最新情報をチェックしてください。