ハイライト
Swift 6 はコンパイル時 data-race 安全保証を実現する新言語モードを導入し、高度に制約された組み込みシステム向けの Embedded Swift サブセットも提供します。
主要内容
2024 年は Swift 公開 10 周年。2014 年 WWDC 初登場から 2015 年のオープンソース化と Linux 対応、Swift 5 の安定 ABI、async/await 並行モデル、C++ 双方向相互運用、Macros まで — Swift は長い進化の道を歩んできました。Swift 6 は可搬性、性能、開発者体験の包括的アップグレードを示します。
Swift 6 最大の変化は新言語モード:コンパイル時 data-race 安全保証。Data-race は並行プログラミングの一般的な落とし穴 — 複数スレッドが可変データに同時アクセスし、一方が変更しようとすると未定義動作になります。Swift 5.10 では complete concurrency checking フラグが必要でしたが、Swift 6 言語モードではデフォルト動作です。非 Sendable 値の actor 境界越えは実行時リスクではなくコンパイルエラーになります。さらに Swift 6 の data-race 検出はより賢く:非 Sendable 値が別 actor に渡された後、元の isolation domain が参照しなければ、コンパイラは安全と認識します。この改善で誤検知が大幅に減り、移行がスムーズになります。
もう一つの重要な方向性は Embedded Swift。高度に制約されたシステム向け言語サブセットで、極小のスタンドアロンバイナリ(数 KB のみ)を生成。反射や any 型などランタイムサポートが必要な言語機能を無効化し、完全なジェネリック特殊化と静的リンクを組み合わせ、ARM と RISC-V マイコン、Apple セキュリティコプロセッサでも動作。C/C++ 組み込み開発に慣れた開発者に、より安全な代替を提供しつつ C/C++ 相互運用で段階的移行を維持します。
詳細
Swift 6 言語モードと Data-race 安全
Swift 6 言語モードの核心目標は data-race 安全をデフォルトにすること。以下は非 Sendable タイプを actor 境界越えに渡す例(28:02):
class Client {
init(name: String, balance: Double) {}
}
actor ClientStore {
static let shared = ClientStore()
private var clients: [Client] = []
func addClient(_ client: Client) {
clients.append(client)
}
}
@MainActor
func openAccount(name: String, balance: Double) async {
let client = Client(name: name, balance: balance)
await ClientStore.shared.addClient(client)
}
キーポイント:
ClientクラスはSendable未マーク、理論上 actor 越え渡しは不安全- Swift 5.10 完全並行チェック下では警告
- Swift 6 コンパイラは
clientがClientStoreに渡された後 MainActor が参照しないため data-race なしと認識 - 渡した後に
clientを再使用するとコンパイラがエラー
Swift 6 は新しい低レベル同期プリミティブも導入。Atomic 型はロックフリー原子操作を提供(28:52):
import Dispatch
import Synchronization
let counter = Atomic<Int>(0)
DispatchQueue.concurrentPerform(iterations: 10) { _ in
for _ in 0 ..< 1_000_000 {
counter.wrappingAdd(1, ordering: .relaxed)
}
}
print(counter.load(ordering: .relaxed))
キーポイント:
Atomicはジェネリック型、プラットフォームがサポートする効率的ロックフリー実装を使用- 並行アクセス安全のため
letプロパティに格納必須 - すべての操作に明示的メモリ順序パラメータが必要、C/C++ メモリモデルに類似
Mutex 型は相互排他を提供(29:21):
import Synchronization
final class LockingResourceManager: Sendable {
let cache = Mutex<[String: Resource]>([:])
func save(_ resource: Resource, as key: String) {
cache.withLock {
$0[key] = resource
}
}
}
キーポイント:
Mutexもletプロパティに格納withLockで保護ストレージへの相互排他アクセスを保証- クロージャ実行全体でロック保持
非コピー可能タイプ(Noncopyable Types)
Swift 6 は非コピー可能タイプのサポートを拡張し、Optional などジェネリックコンテキストで使用可能(17:50):
struct File: ~Copyable {
private let fd: CInt
init?(name: String) {
guard let fd = open(name) else {
return nil
}
self.fd = fd
}
func write(buffer: [UInt8]) {
// ...
}
deinit {
close(fd)
}
}
キーポイント:
~Copyableはコピー不可を示し、一意所有シナリオに適する- Swift 5.10 は具象タイプのみ、Swift 6 はジェネリックコンテキスト対応
Optional<File>を返す失敗可能イニシャライザを記述可能deinitでスコープ終了時にファイル記述子をクローズ
Typed Throws
Swift 6 は型付き throw を導入し、関数がエラータイプを指定可能(23:43 対 24:19):
enum IntegerParseError: Error {
case nonDigitCharacter(String, index: String.Index)
}
// 従来の throws(型消去)
func parse(string: String) throws -> Int {
for index in string.indices {
throw IntegerParseError.nonDigitCharacter(string, index: index)
}
}
// Typed throws(型を保持)
func parse(string: String) throws(IntegerParseError) -> Int {
for index in string.indices {
throw IntegerParseError.nonDigitCharacter(string, index: index)
}
}
// 使用時、error はそのまま IntegerParseError 型
do {
let value = try parse(string: "1+234")
}
catch {
// error の型は IntegerParseError で、any Error ではない
}
キーポイント:
throws(ErrorType)構文で throw するエラータイプを指定- 型消去なし、catch ブロックの error は具体型を保持
throwsはthrows(any Error)と同等、非 throw 関数はthrows(Never)と同等- 内部関数や制約環境(ランタイム割り当てなし)に適する
クロスプラットフォームと静的 Linux SDK
Swift 6 は macOS から Linux へのクロスコンパイルをサポートする新静的 Linux SDK を導入(9:15):
# macOS 版をビルド
swift build
# 静的 Linux SDK をインストール
swift sdk install ~/preview-static-swift-linux-0.0.1.tar.gz
# ARM64 Linux 向けにクロスコンパイル(静的リンク)
swift build --swift-sdk aarch64-swift-linux-musl
# 出力を確認
file .build/debug/CatService
# Linux へデプロイ
scp .build/debug/CatService demo-linux-host:~/CatService
# Linux 上で実行(Swift ランタイムのインストール不要)
./CatService
キーポイント:
- 静的リンクにより任意の Linux マシンでバイナリ実行可能
- ターゲットマシンに Swift ランタイムや依存ライブラリ不要
muslは静的リンク用の軽量 C 標準ライブラリ- Fedora や Debian など新 Linux プラットフォームをサポート
Swift Testing
新 Swift Testing フレームワークはよりモダンなテスト体験を提供(13:50):
import Testing
@Test("Recognized rating", .tags(.critical))
func rating(videoId: Int, videoName: String, expectedRating: String) {
let video = Video(id: videoId, name: videoName)
#expect(video.rating == expectedRating)
}
キーポイント:
@Testマクロでテスト関数をマーク、表示名をカスタマイズ可能.tags()でテストを整理・フィルタ- パラメータ化テストで複数入力のコード重複を回避
#expectマクロは任意の Swift 式をサポート
重要ポイント
1. Swift 6 言語モードへ段階的移行
プロジェクト全体を急いで切り替えない。Build Settings で並行チェックを段階的に有効化(SWIFT_STRICT_CONCURRENCY を minimal または targeted に設定)し、問題を修正してから厳格度を上げる。Swift 6 の賢い data-race チェックで誤検知が大幅減少し移行がスムーズ。まだ変更したくないタイプにはコンパイラが支援ツールを提供。
2. リソース制約シナリオで Embedded Swift を検討
マイコン、組み込みシステム、極小二進サイズが必要な場合、Embedded Swift は C/C++ 以外の安全な代替。Playdate ゲーム機は数 KB の Swift ゲームを実行可能、Apple セキュリティコプロセッサも使用。C++ 相互運用で既存組み込みコードに段階的に Swift を導入。
3. 静的 Linux SDK でデプロイを簡素化
macOS で開発、Linux へデプロイ — 静的リンクバイナリはターゲットマシンに Swift ランタイム不要。コンテナ化デプロイや CI/CD に非常に適する。swift build --swift-sdk aarch64-swift-linux-musl 一行でどこでも動く実行ファイルを生成。
関連セッション
- A Swift Tour: Explore Swift’s features and design — Swift 言語の核心機能と設計哲学の入門
- Migrate your app to Swift 6 — 既存 app を Swift 6 言語モードへ移行する実践ガイド
- Meet Swift Testing — 新 Swift Testing フレームワークの使い方とベストプラクティス
- Go small with Embedded Swift — マイコンとリソース制約システムでの Embedded Swift
- Mix Swift and C++ — Swift と C++ 双方向相互運用の深掘り(2023)
コメント
GitHub Issues · utterances