WWDC Quick Look 💓 By SwiftGGTeam
What's new in Swift

What's new in Swift

元の動画を見る

ハイライト

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 コンパイラは clientClientStore に渡された後 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
    }
  }
}

キーポイント:

  • Mutexlet プロパティに格納
  • 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:4324: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 は具体型を保持
  • throwsthrows(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_CONCURRENCYminimal または 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 一行でどこでも動く実行ファイルを生成。


関連セッション

コメント

GitHub Issues · utterances