この記事の要点
- Swift 6.1 がリリースされました。生産性を高める言語拡張、診断の改善、パッケージの trait、そしてデータ競合安全性の使い勝手とコンパイル時間を改善する継続的な取り組みが含まれます。
- 言語面では、
nonisolatedを型やエクステンションにも書けるようになり、task group の子タスク結果型が推論されるようになりました。また Objective-C の実装を Swift で書く@implementation、末尾カンマを書ける場所の拡大も加わりました。 - パッケージ面では、Embedded Swift や WebAssembly などの環境ごとに異なる API・機能を提供できる package traits が導入され、SourceKit-LSP の background indexing がデフォルトで有効になりました。
- Swift Testing には、テストの前後処理を共有できる test scoping trait と、捕捉したエラーを返す
#expect(throws:)/#require(throws:)が加わりました。
主な変更点
言語と標準ライブラリ
並行処理: 型・エクステンションへの nonisolated
Swift の並行処理モデルでは、プロパティや関数に nonisolated を書くことで、その API が任意の並行コンテキストから安全に呼び出せることを示せます。Swift 6.1 では nonisolated を型やエクステンションにも書けるようになりました。これにより、型への @MainActor 推論を防いだり、エクステンション内のすべてのメソッドに個別の注釈なしで nonisolated を適用したりできます。
@MainActor
struct S {
let id: Int
let name: String
// ミュータブルな状態と MainActor のメソッド
}
nonisolated extension S: CustomStringConvertible, Equatable {
var description: String {
"id: \(id), name: \(name)"
}
static func ==(lhs: S, rhs: S) -> Bool {
lhs.id == rhs.id
}
}
詳しくは SE-0449 のダイジェストを参照してください。
並行処理: task group の結果型推論
Swift 6.1 では withTaskGroup と withThrowingTaskGroup の子タスク結果型が推論されるようになりました。従来は task group を作成する際に、子タスクの結果型を引数として必ず書く必要がありました。
let messages = await withTaskGroup(of: Message.self) { group in
for id in ids {
group.addTask { await downloadMessage(for: id) }
}
var messages: [Message] = []
for await message in group {
messages.append(message)
}
return messages
}
Swift 6.1 では、子タスクの結果型を task group のクロージャから推論できます。
// 以前のような (of: Message.self) は不要
let messages = await withTaskGroup { group in
for id in ids {
group.addTask { await downloadMessage(for: id) }
}
var messages: [Message] = []
for await message in group {
messages.append(message)
}
return messages
}
データ競合安全性の親しみやすさは引き続き活発に開発が進んでいる領域です。方針の全体像はデータ競合安全性の親しみやすさを高めるための vision ドキュメントにまとめられており、関連する多くの Swift Evolution Proposal がレビュー中です。
Objective-C の型を Swift で実装する
Swift 6.1 では新しい属性 @implementation が加わりました。@objc と組み合わせて、Objective-C からインポートした宣言に対する実装を提供するために使えます。Swift の extension に @objc @implementation を書くと、Objective-C の @implementation ブロックを置き換えられます。
ヘッダは通常どおり Objective-C のクラスとして書きますが、実装は Objective-C ファイルの @implementation ではなく、Swift ファイルの @objc @implementation extension として書きます。既存クラスの実装を、後方互換性を壊さずにカテゴリ単位で少しずつ Swift へ移植することもできます。詳しくは SE-0436 のダイジェストを参照してください。
生産性の向上: 末尾カンマの拡大
Swift では、コレクションリテラルに末尾カンマを書くことで、最後の要素もほかの要素と同じように追加・削除・並べ替え・コメントアウトしやすくなっています。
let rank = [
"Player 1",
"Player 3",
"Player 2",
]
Swift 6.1 では、末尾カンマをタプル、パラメータリストと引数リスト、ジェネリックパラメータリスト、クロージャのキャプチャリスト、文字列補間にも書けるように拡張されました。
let numbers = [1, 2, 0, 3, 4, 0, 0, 5]
let subsequences = numbers.split(
separator: 0,
maxSplits: 1,
)
開発の手触りが良くなるだけでなく、プラグインやマクロのようなコード生成ツールも単純化できます。カンマ区切りのリストを生成する際に、最後の要素だけを特別扱いする条件分岐が不要になるためです。詳しくは SE-0439 のダイジェストを参照してください。
パッケージとビルドの改善
Swift 6.1 では package traits が導入されました。これは、Embedded Swift や WebAssembly といった特定の環境で使われるときに、異なる API・機能を提供できる新しいパッケージ設定です。パッケージの作者は Package.swift で自分のパッケージが提供する trait の集合を定義でき、条件付きコンパイルやオプションの依存関係を表現できます。パッケージはデフォルトで有効になる trait を指定でき、利用側は依存関係を宣言する際に使う trait をカスタマイズできます。
dependencies: [
.package(
url: "https://github.com/Org/SomePackage.git",
from: "1.0.0",
traits: [
.default, // パッケージのデフォルト trait をすべて有効にする
"Embedded"
]
),
]
詳しくは SE-0450 のダイジェストを参照してください。
ジャンプ先の定義表示など、多くの編集機能はインデックスによって支えられています。Swift 6.1 より前は、インデックスはプロジェクトのビルド時に行われていたため、ライブラリへの追加・変更がこれらの編集機能に反映されるのはビルド後でした。Swift 6.1 では、SourceKit-LSP の SwiftPM プロジェクトに対して background indexing がデフォルトで有効になり、プロジェクトを変更するたびにモジュール横断・グローバルな機能が最新に保たれます。
Swift Testing
Swift 6.1 では、テストの実行前後にロジックを差し込んで、セットアップ・後始末の処理を共有できるカスタム trait が使えるようになりました。新しい TestScoping プロトコルに適合するカスタム trait 型を書くと、その trait が適用された各テストやスイートの実行スコープをカスタマイズするメソッドを実装できます。たとえば、task local な値をモックのリソースに束縛する trait を実装できます。
struct MockAPICredentialsTrait: TestTrait, TestScoping {
func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
let mockCredentials = APICredentials(apiKey: "fake")
try await APICredentials.$current.withValue(mockCredentials) {
try await function()
}
}
}
extension Trait where Self == MockAPICredentialsTrait {
static var mockAPICredentials: Self { Self() }
}
@Test(.mockAPICredentials)
func example() {
// APICredentials.current を参照しつつ API の利用を検証する...
}
詳しくは ST-0007 のダイジェストを参照してください。また Swift 6.1 の Swift Testing には、捕捉したエラーを返すように改良された #expect(throws:) と #require(throws:) マクロも含まれます。テスト関数内でエラーを検査しやすくなりました(ST-0006)。
Swift-DocC
Swift-DocC には、パラメータ型と戻り値型の情報に基づく、より人間にとって読み書きしやすいシンボルリンクの曖昧性解消の方法が加わりました。たとえば、パラメータ型と戻り値型が異なる次の 3 つのオーバーロードを考えます。
func doSomething(first: String, second: Int) -> Double
func doSomething(first: String?, second: Int) -> Float
func doSomething(first: String?, second: Int) -> Double
従来は、これらのオーバーロードへのリンクを書く際、特定のオーバーロードを一意に参照するために、シンボルの一意な識別子の短いハッシュ(-3c5j など)を含める必要がありました。このハッシュからどのオーバーロードを指すのかを人間が読み取ることはできません。Swift 6.1 では、-(String,_)、->Float、-(String?,_)->Double のように、パラメータ型と戻り値型の組み合わせを使ってリンクの曖昧性を解消し、特定のオーバーロードを一意に参照できます。
各オーバーロードを区別する最小限のパラメータ型・戻り値型の組み合わせは、曖昧なシンボルリンクに関する Swift-DocC の警告から知ることができます。
Swift 利用者への影響
- 並行処理の注釈を書きやすくなります。
nonisolatedを型やエクステンションに書けるようになったことで、@MainActor推論を型単位で防いだり、エクステンション全体に一括でnonisolatedを適用したりできます。task group の結果型推論により、withTaskGroup(of:)の型引数も省略できます。 - Objective-C との連携が書きやすくなります。
@objc @implementationにより、Objective-C のクラス実装を、後方互換性を保ったまま少しずつ Swift へ移植できます。 - 末尾カンマで編集とコード生成が楽になります。 タプル・引数リスト・ジェネリックパラメータなど多くの場所で末尾カンマを書けるようになり、行の追加・削除・並べ替えが容易になります。マクロやプラグインの実装も単純化できます。
- パッケージを環境ごとに最適化できます。 package traits により、Embedded Swift や WebAssembly などの環境ごとに API・機能・依存関係を切り替えられます。
- テストの前後処理を共有できます。
TestScopingtrait でセットアップ・後始末を共通化でき、改良された#expect(throws:)/#require(throws:)で捕捉したエラーを検査しやすくなります。
関連 Proposal・リンク
- 前のリリースについては Swift 6 の発表 を参照してください。
- 本記事で触れた Proposal ダイジェスト:
- Swift 6.1 に含まれる言語進化の Proposal 一覧: Swift Evolution dashboard
- Swift のインストール