swift-concurrency について
swift-concurrencyは、隔離、Sendable、タスク、テストを扱うSwift開発者向けの並行処理ガイドスキルです。プロジェクト設定と具体的な診断を確認し、安全性を保つ最小限の修正を重視します。
主な機能
- 01Swiftの並行処理ビルド設定の分析
- 02隔離境界と診断内容の特定
- 03一般的なエラーの修正案内
- 04テーマに応じたリファレンス案内
- 05Swift 6移行と検証のガイド
活用シーン
- 01MainActor隔離エラーの調査
- 02Sendableの受け渡し問題の解決
- 03Swift 6並行処理移行の計画
- 04ビルドとテストによる変更の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ avdlee/ swift-concurrency-agent-skill --skill swift-concurrencyインストール後、Claude Code で/swift-concurrencyを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/_index.mdactors.mdasync-algorithms.mdasync-await-basics.mdasync-sequences.mdcore-data.mdglossary.mdlinting.mdmemory-management.mdmigration.mdobservation.mdperformance.mdsendable.mdtasks.mdtesting.mdthreading.md詳細
Swift Concurrency
Fast Path
修正を提案する前に、次の手順を実施してください。
Package.swiftまたは.pbxprojを解析し、Swift 言語モード、厳格な並行処理のレベル、デフォルトの分離設定、今後導入予定の機能を確認します。移行作業に限らず、必ず確認してください。- 診断内容と問題のシンボルを正確に記録します。
- 分離境界を特定します。
@MainActor、カスタムアクター、アクターインスタンスの分離、またはnonisolatedのいずれかを確認してください。 - コードが UI に紐づくものか、メインアクター以外で実行する想定なのかを確認します。非構造化タスクを作成する際は、同期的なプレフィックス(最初の
awaitより前のすべての処理)を確認してください。このプレフィックスで本当にメインアクターへのアクセスが必要な場合に限り、@MainActorで開始します。それ以外の場合はTask { @concurrent in ... }を使い、中断後にのみMainActor.runで切り替えます。単純なメインアクター以外の処理(たとえばprint)の後、同じプレフィックス内でメインアクター上の処理を行うからといって、@concurrentを使う理由にはなりません。遅延再試行、タイマー、バックオフのタスクでは、待機と UI の変更を分けてください。最終的な状態更新がメインアクター上で行われる場合でも、多くの場合、スリープはメインアクター以外で行うべきです。
並行処理の動作を変えるプロジェクト設定:
| 設定 | SwiftPM(Package.swift) | Xcode(.pbxproj) |
|---|---|---|
| 言語モード | swiftLanguageVersions または -swift-version(// swift-tools-version: は信頼できる目安ではありません) | Swift 言語バージョン |
| 厳格な並行処理 | .enableExperimentalFeature("StrictConcurrency=targeted") | SWIFT_STRICT_CONCURRENCY |
| デフォルトの分離設定 | .defaultIsolation(MainActor.self) | SWIFT_DEFAULT_ACTOR_ISOLATION |
| 今後導入予定の機能 | .enableUpcomingFeature("NonisolatedNonsendingByDefault") | SWIFT_UPCOMING_FEATURE_* |
| Approachable Concurrency | 該当なし(個別の今後導入予定の機能を使用) | SWIFT_APPROACHABLE_CONCURRENCY |
Xcode 26 に関する注意:Xcode 26 で作成された新しいプロジェクトでは、
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActorとSWIFT_APPROACHABLE_CONCURRENCY = YESがデフォルトで有効になっていることがよくあります。これらは新規プロジェクトで有力なデフォルト設定として扱い、確認済みの設定とはみなさないでください。
これらの設定が不明な場合は、移行に影響するガイダンスを提示する前に、開発者に確認を求めてください。Xcode 26 の新しいプロジェクトであっても、推測してはいけません。
ガードレール:
- 一律の修正として
@MainActorを推奨しないでください。コードが本当に UI に紐づく理由を説明してください。 - 非構造化タスクより、構造化並行処理を優先してください。明確な理由がある場合に限り、
Task.detachedを使います。 @preconcurrency、@unchecked Sendable、またはnonisolated(unsafe)を推奨する場合は、安全性を保証する不変条件を文書化し、後で削除する計画を立てるよう求めてください。- 変更は安全な範囲で最小限にとどめてください。移行の際に、無関係なアーキテクチャをリファクタリングしないでください。
- コースへの参照は、理解を深めるためだけに使います。開発者の質問への回答に明確に役立つ場合に限り、控えめに使用してください。
Quick Fix Mode
次の条件をすべて満たす場合は、Quick Fix Mode を使用してください。
- 問題が単一のファイルまたは型に限定されている。
- 分離境界が明確である。
- 動作を維持したまま、1~2 段階で修正を説明できる。
次のいずれかに該当する場合は、Quick Fix Mode を使用しないでください。
- ビルド設定またはデフォルトの分離設定が不明である。
- 問題がモジュール間にまたがる、または公開 API の動作を変える。
- 安全でない回避策に依存する可能性が高い。
よくある診断
| 診断 | 最初に確認すること | 最小限で安全な修正 | 次に参照するもの |
|---|---|---|---|
Main actor-isolated ... cannot be used from a nonisolated context | これは本当に UI に関係する処理ですか? | 呼び出し元を @MainActor に分離するか、メインアクターが所有するのが適切な場合に限り await MainActor.run { ... } を使用します。 | references/actors.md、references/threading.md |
Actor-isolated type does not conform to protocol | 要件をアクター上で実行する必要がありますか? | 分離された適合を優先します(例:extension Foo: @MainActor SomeProtocol)。nonisolated を使うのは、本当に非分離である必要がある要件に限ります。 | references/actors.md |
Sending value of non-Sendable type ... risks causing data races | どの分離境界を越えていますか? | 1 つのアクター内でアクセスを完結させるか、転送する値を不変の値型に変換します。 | references/sendable.md、references/threading.md |
SwiftLint async_without_await | async はプロトコル、オーバーライド、または @concurrent によって本当に必要ですか? | async を削除するか、理由を明記した限定的な抑制を使います。見せかけの await は追加しないでください。 | references/linting.md |
wait(...) is unavailable from asynchronous contexts | これは従来の XCTest の非同期待機ですか? | await fulfillment(of:) または Swift Testing の同等の機能に置き換えます。 | references/testing.md |
| Core Data の並行処理に関する警告 | NSManagedObject インスタンスがコンテキスト間またはアクター間を移動していますか? | NSManagedObjectID を渡すか、Sendable な値型にマッピングします。 | references/core-data.md |
@Observable の分離または Sendable に関するエラー | @Observable クラスに正しいアクターが注釈されていますか? | UI の状態には @MainActor を追加し、境界を越えて渡す場合は Sendable なスナップショットを使います。 | references/observation.md |
Thread.current unavailable from asynchronous contexts | 分離ではなくスレッドを使ってデバッグしていませんか? | 分離の観点から考え、代わりに Instruments/デバッガーを使います。 | references/threading.md |
| SwiftLint の並行処理関連の警告 | どの lint ルールが発生しましたか? | ルールの意図と推奨される修正については references/linting.md を参照し、ダミーの await は避けます。 | references/linting.md |
... cannot satisfy conformance requirement for a 'Sendable' type parameter (SendableMetatype) | 適合にグローバルアクターによる分離が伴っていますか? | 適合からアクターによる分離を削除するか、分離境界を越えてメタタイプを渡さないようにします。references/actors.md の SendableMetatype セクションを参照してください。 | references/actors.md |
クイック修正で解決しない場合
- まだ確認していない場合は、プロジェクト設定を収集します。
- 型がどの分離境界を越えるのかを再評価します。
- より詳しい修正について、該当するリファレンスファイルを参照します。
- 修正によって動作が変わる可能性がある場合は、不変条件を文書化し、検証手順を追加します。
最小限で安全な修正
データ競合の安全性を確保しながら、動作を維持できる変更を優先します。
- UI に関係する状態:型またはメンバーを
@MainActorに分離します。 - 共有される可変状態:その状態を
actorの背後に移すか、UI が所有する状態である場合に限り@MainActorを使います。 - バックグラウンド処理:処理を呼び出し元の分離から切り離す必要がある場合は、
@concurrentが付いたasyncAPI を使います。処理が呼び出し元の分離を安全に継承できる場合は、@concurrentを付けずにnonisolatedを使います。Taskを生成する場合は、開始時の分離を同期的な前半部分に合わせます。最初のawaitより前にメインアクターを必要とする処理がない場合は、Task { @concurrent in ... }を使い、UI の更新にはawait MainActor.run { ... }で戻ります。前半部分に、単純な非メインアクター処理とメインアクター上の処理が混在する場合は、継承された@MainActorの開始を維持します。軽量な行をメインアクターの外に切り離す価値は、余分な切り替えに見合いません。 - Sendability の問題:
@unchecked Sendableより、不変の値と明示的な境界を優先します。
並行処理ツールの選択
| 必要な処理 | ツール | 主な指針 |
|---|---|---|
| 単一の非同期処理 | async/await | 順次実行する非同期処理の既定の選択肢です |
| 固定数の並列処理 | async let | コンパイル時に個数がわかっている場合に使用します。スロー時には自動キャンセルされます |
| 動的な並列処理 | withTaskGroup | 個数が不明な場合に使用します。構造化されており、スコープ終了時に子タスクをキャンセルします |
| 同期処理から非同期処理へのブリッジ | Task { } | アクターコンテキストを継承します。Task.detached は、理由を文書化できる場合にのみ使用してください |
| 共有する可変状態 | actor | ロックやキューよりも優先してください。隔離されたセクションは小さく保ちます |
| UI に関連する状態 | @MainActor | 本当に UI 関連のコードにのみ使用し、隔離の理由を明確にしてください |
よくあるシナリオ
UI を更新するネットワークリクエスト
Task { @concurrent in
let data = try await fetchData()
await MainActor.run { self.updateUI(with: data) }
}
配列の項目を並列処理する
await withTaskGroup(of: ProcessedItem.self) { group in
for item in items {
group.addTask { await process(item) }
}
for await result in group {
results.append(result)
}
}
Task の開始時の隔離
Task の開始時の隔離を、その同期処理部分({ から最初の await までのすべて)に合わせます。
- その部分で
@MainActorが必要な処理がある場合は、継承した@MainActorで開始します。 - その部分で
@MainActorが必要な処理がない場合は、Task { @concurrent in ... }を優先し、UI が所有する変更を行う場合にのみ戻ります。
// ❌ Synchronous prefix is empty; first work hops away
Task {
await hopToOtherIsolationDomain()
}
// ❌ Synchronous prefix is only `print` (trivial, non-main); first await hops away
Task {
print("Also not main-thread-bound")
await hopToOtherIsolationDomain()
}
// ✅ Start off the main actor, hop back only for UI work
Task { @concurrent in
await hopToOtherIsolationDomain()
await MainActor.run { updateUI() }
}
// ✅ Synchronous prefix DOES contain main-actor work — keep inheritance
Task {
print("debug") // trivial, non-main — rides along
self.isLoading = true // needs @MainActor, before any await
await fetchData()
}
Swift 6 移行のクイックガイド
Swift 6 の主な変更点:
- 厳密な並行処理チェックがデフォルトで有効
- 完全なデータ競合の安全性をコンパイル時に確保
- 境界でSendable 要件を適用
- すべての非同期境界で隔離チェックを実施
移行の検証サイクル
移行時の変更ごとに、次のサイクルを適用します。
- ビルド —
swift buildまたは Xcode のビルドを実行し、新しい診断を確認します - 修正 — 一度に 1 つのエラーカテゴリに対応します(例:まず Sendable に関する問題をすべて修正します)
- 再ビルド — 次に進む前に、修正が問題なくコンパイルされることを確認します
- テスト — テストスイートを実行し、リグレッションを検出します(
swift testまたは Cmd+U) - 次へ進むのはすべての診断が解決してからです。次のファイル/モジュールに進みます
修正によって新しい警告が発生した場合は、続行する前に解決してください。関連のない複数の修正をまとめて行わず、コミットは小さく、レビューしやすい状態に保ってください。
詳しい移行手順については、references/migration.md を参照してください。
リファレンス案内
質問に合致する、最小限のリファレンスを開いてください。
- 基礎
references/async-await-basics.md— async/await の構文、実行順序、async let、URLSession のパターンreferences/tasks.md— Task のライフサイクル、キャンセル、優先度、タスクグループ、構造化/非構造化の違いreferences/actors.md— アクター隔離、@MainActor、グローバルアクター、再入可能性、カスタムエグゼキューター、Mutexreferences/sendable.md— Sendable への準拠、値型/参照型、@unchecked、リージョン隔離references/threading.md— 実行モデル、中断ポイント、Swift 6.2 の隔離動作
- ストリーム
references/async-sequences.md— AsyncSequence、AsyncStream、通常の非同期メソッドとの使い分けreferences/async-algorithms.md— デバウンス、スロットル、マージ、combineLatest、チャネル、タイマー
- 実践的なトピック
references/testing.md— Swift Testing を優先し、XCTest を代替として使用する方法、リークチェックreferences/performance.md— Instruments によるプロファイリング、中断ポイントの削減、実行戦略references/memory-management.md— タスク内の強参照循環、メモリ安全性のパターンreferences/core-data.md— NSManagedObject の送信可能性、カスタムエグゼキューター、隔離の競合references/observation.md— @MainActor と組み合わせた @Observable、隔離をまたぐアクセス、Sendable の制約
- 移行とツール
references/migration.md— Swift 6 の移行戦略、クロージャから async への変換、@preconcurrency、FRP の移行references/linting.md— 並行処理に重点を置いた lint ルールと SwiftLint のasync_without_await
- 用語集
references/glossary.md— 並行処理の主要用語の簡単な定義
検証チェックリスト
並行処理コードを変更する場合:
- 診断結果を解釈する前に、ビルド設定を再確認します。
- 次に進む前に、エラーのカテゴリを1つずつビルドして解消します。無関係な修正を同じ変更にまとめないでください。
- テストを実行します。特に、アクター、ライフタイム、キャンセルの影響を受けやすいテストを実行してください。
- パフォーマンスに関する主張は推測せず、Instruments を使用して検証します。
- 長時間実行されるタスクの解放動作とキャンセル動作を検証します。
- 長時間実行される処理では
Task.isCancelledを確認します。 - アクターによる分離または
Mutexのほうが所有権を安全に表現できる場合、非同期コンテキストでセマフォやその場しのぎのロックを使用しないでください。
注:このスキルは、Antoine van der Lee による包括的な Swift Concurrency Course に基づいています。