swift-concurrency について
swift-concurrencyは、Swiftの並行コードの作成と移行を支援するスキルです。隔離、構造化並行処理、Sendable、テスト、性能、Swift 6への移行を、プロジェクト設定に応じて扱います。
主な機能
- 01async/awaitとタスクのパターンを適用
- 02アクターと隔離境界を分析
- 03Sendableとデータ競合安全性に対応
- 04並行処理エラーとlint警告を診断
- 05Swift 6移行と検証を計画
活用シーン
- 01並列処理に適したパターンの選択
- 02隔離またはSendableのコンパイルエラー修正
- 03非同期テストと性能問題の確認
- 04SwiftプロジェクトをSwift 6へ移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wendylabsinc/ claude-skills --skill swift-concurrencyインストール後、Claude Code で/swift-concurrencyを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/actors.mdasync-await-basics.mdasync-sequences.mdglossary.mdperformance.mdproduction-patterns.mdsendable.mdtasks.mdtesting.mdthreading.md詳細
Swift Concurrency
概要
このスキルでは、Swift Concurrency に関する専門的なガイダンスを提供します。最新の async/await パターン、アクター、タスク、Sendable への適合、Swift 6 への移行を扱います。開発者が安全で高性能な並行コードを記述し、Swift の構造化並行性モデルの複雑さに対応できるよう支援するために、このスキルを使用してください。
エージェントの行動規範(以下のルールに従ってください)
- 助言がプロジェクト設定に依存する場合は、プロジェクトまたはパッケージのファイルを分析し、使用されている Swift 言語モード(Swift 5.x または Swift 6)と Xcode/Swift ツールチェーンを確認してください。
- 修正を提案する前に、分離境界を特定してください。
@MainActor、カスタムアクター、アクターインスタンスの分離、または非分離のいずれかです。 - 一律の解決策として
@MainActorを推奨しないでください。そのコードにメインアクター分離が適切な理由を説明してください。 - 非構造化タスクより、構造化並行性(子タスク、タスクグループ)を優先してください。明確な理由がある場合に限り、
Task.detachedを使用してください。 @preconcurrency、@unchecked Sendable、またはnonisolated(unsafe)を推奨する場合は、以下を必須としてください。- 文書化された安全性の不変条件
- 削除または移行するためのフォローアップチケット
- 移行作業では、影響範囲を最小限に抑え(小規模でレビューしやすい変更にし)、検証手順を追加してください。
プロジェクト設定の確認(助言の前に評価してください)
並行性の挙動はビルド設定に依存します。必ず次の項目の確認を試みてください。
- デフォルトのアクター分離(モジュールのデフォルトは
@MainActorですか、それともnonisolatedですか?) - 厳密な並行性チェックのレベル(minimal/targeted/complete)
- 今後の機能が有効になっているか(特に
NonisolatedNonsendingByDefault) - Swift 言語モード(Swift 5.x または Swift 6)と SwiftPM のツールバージョン
手動での確認(スクリプト不要)
- SwiftPM:
.defaultIsolation(MainActor.self)があるかPackage.swiftを確認してください。.enableUpcomingFeature("NonisolatedNonsendingByDefault")があるかPackage.swiftを確認してください。- 厳密な並行性に関するフラグを確認してください。.enableExperimentalFeature("StrictConcurrency=targeted")`(または同様の設定)が対象です。
- 先頭にあるツールバージョンを確認してください:
// swift-tools-version: ...
- Xcode プロジェクト:
project.pbxprojで次を検索してください。SWIFT_DEFAULT_ACTOR_ISOLATIONSWIFT_STRICT_CONCURRENCYSWIFT_UPCOMING_FEATURE_(および/またはSWIFT_ENABLE_EXPERIMENTAL_FEATURES)
これらが不明な場合は、移行に関わる助言を行う前に、開発者に確認を求めてください。
クイック判断ツリー
開発者が並行性に関するガイダンスを必要としている場合は、次の判断ツリーに従ってください。
-
async コードを新たに書き始めますか?
- 基本的なパターンについては
references/async-await-basics.mdを参照してください。 - 並列処理については
references/tasks.md(async let、タスクグループ)を参照してください。
- 基本的なパターンについては
-
共有される可変状態を保護しますか?
- クラスベースの状態を保護する必要がある場合 →
references/actors.md(アクター、@MainActor)を参照してください。 - スレッドセーフな値の受け渡しが必要な場合 →
references/sendable.md(Sendable への適合)を参照してください。
- クラスベースの状態を保護する必要がある場合 →
-
非同期処理を管理しますか?
- 構造化された非同期処理 →
references/tasks.md(Task、子タスク、キャンセル)を参照してください。 - ストリーミングデータ →
references/async-sequences.md(AsyncSequence、AsyncStream)を参照してください。
- 構造化された非同期処理 →
-
パフォーマンスまたはデバッグに関する問題ですか?
- 非同期コードが遅い場合 →
references/performance.md(プロファイリング、中断ポイント)を参照してください。 - テストに関する懸念 →
references/testing.md(XCTest、Swift Testing)を参照してください。
- 非同期コードが遅い場合 →
トリアージ優先プレイブック(よくあるエラー → 次に取るべき対応)
- SwiftLint の並行処理関連の警告
- ルールの意図と推奨される修正については
references/linting.mdを参照してください。「修正」としてダミーの await を追加することは避けてください。
- ルールの意図と推奨される修正については
- SwiftLint の
async_without_await警告- 必要でなければ
asyncを削除してください。プロトコル、オーバーライド、または @concurrent のために必要な場合は、偽の await を追加するよりも、対象を限定した警告抑制を優先してください。references/linting.mdを参照してください。
- 必要でなければ
- 「Sendable でない型 ... の値を送信すると、データ競合が発生するリスクがあります」
- まず、その値がどこで隔離境界を越えるかを特定します
- 次に、
references/sendable.mdとreferences/threading.md(特に Swift 6.2 の動作変更)を参照します
- 「メインアクターで隔離された ... は、非隔離コンテキストから使用できません」
- まず、本当に
@MainActorに属するべきか判断します - 次に、
references/actors.md(グローバルアクター、nonisolated、隔離パラメーター)とreferences/threading.md(デフォルトの隔離)を参照します
- まず、本当に
- 「クラスプロパティ『current』は非同期コンテキストから使用できません」(Thread API)
- スレッド中心のデバッグを避け、隔離と Instruments に頼るため、
references/threading.mdを参照します
- スレッド中心のデバッグを避け、隔離と Instruments に頼るため、
- 「wait(...) は非同期コンテキストから使用できません」のような XCTest の非同期エラー
references/testing.md(await fulfillment(of:)と Swift Testing のパターン)を参照します
- Core Data の並行処理に関する警告/エラー
references/core-data.md(DAO/NSManagedObjectID、デフォルトの隔離との競合)を参照します
コアパターンのリファレンス
各並行処理ツールの使い分け
async/await - 既存の同期コードを非同期にする
// Use for: Single asynchronous operations
func fetchUser() async throws -> User {
try await networkClient.get("/user")
}
async let - 複数の独立した非同期処理を並行して実行する
// Use for: Fixed number of parallel operations known at compile time
async let user = fetchUser()
async let posts = fetchPosts()
let profile = try await (user, posts)
Task Group - 構造化並行処理で動的な並行処理を行う
// Use for: Unknown number of parallel operations at compile time
await withTaskGroup(of: Result.self) { group in
for item in items {
group.addTask { await process(item) }
}
}
Actor - データ競合から可変状態を保護する
// Use for: Shared mutable state accessed from multiple contexts
actor DataCache {
private var cache: [String: Data] = [:]
func get(_ key: String) -> Data? { cache[key] }
}
@MainActor - UI の更新をメインスレッドで行う
// Use for: View models, UI-related classes
@MainActor
class ViewModel: ObservableObject {
@Published var data: String = ""
}
よくあるシナリオ
シナリオ:複数のネットワークリクエストを並行して実行する
async let users = fetchUsers()
async let posts = fetchPosts()
async let comments = fetchComments()
let (u, p, c) = try await (users, posts, comments)
シナリオ:配列の要素を並行して処理する
await withTaskGroup(of: ProcessedItem.self) { group in
for item in items {
group.addTask { await process(item) }
}
for await result in group {
results.append(result)
}
}
Swift 6 移行クイックガイド
Swift 6 の主な変更点:
- 厳密な並行処理チェックがデフォルトで有効
- コンパイル時の完全なデータ競合安全性
- 境界におけるSendable 要件の適用
- すべての非同期境界での隔離チェック
詳しい移行手順については、references/migration.md を参照してください。
リファレンスファイル
特定のトピックに応じて、以下のファイルを読み込んでください。
async-await-basics.md- async/await の構文、実行順序、async let、URLSession のパターンtasks.md- Task のライフサイクル、キャンセル、優先順位、タスクグループ、構造化/非構造化並行処理threading.md- スレッドとタスクの関係、一時停止ポイント、隔離ドメイン、nonisolatedactors.md- Actor の隔離、@MainActor、グローバルアクター、再入可能性、カスタムエグゼキューター、Mutexsendable.md- Sendable への準拠、値型/参照型、@unchecked、リージョン隔離async-sequences.md- AsyncSequence、AsyncStream、通常の非同期メソッドとの使い分けperformance.md- Instruments によるプロファイリング、一時停止ポイントの削減、実行戦略testing.md- XCTest の非同期パターン、Swift Testing、並行処理テスト用ユーティリティproduction-patterns.md- 高度なパターン:Actor の状態マシン、~Copyable 状態、構造化並行処理、ロックフリーのアトミック操作、サービスのライフサイクル
ベストプラクティスのまとめ
- 構造化並行処理を優先する - 可能な場合は、非構造化タスクよりタスクグループを使用します
- 一時停止ポイントを最小限にする - コンテキスト切り替えを減らすため、Actor で隔離されたセクションを小さく保ちます
- @MainActor は慎重に使う - 本当に UI に関連するコードにのみ使用します
- 型を Sendable にする - Sendable に準拠させ、安全な並行アクセスを可能にします
- キャンセルに対応する - 実行時間の長い処理では Task.isCancelled を確認します
- ブロッキングを避ける - 非同期コンテキストではセマフォやロックを決して使用しません
- 並行処理コードをテストする - 適切な非同期テストメソッドを使用し、タイミングの問題も考慮します
リソース管理には構造化された並行処理を使用する
クリーンアップが必要なリソース(接続、ファイルハンドルなど)を管理する場合は、構造化された並行処理によって適切にクリーンアップされるよう、withX パターンを使用してください。
悪い例 — 構造化されていないクリーンアップ:
func withConnection<R>(
_ body: (Connection) async throws -> R
) async throws -> R {
let connection = Connection()
try await connection.connect()
defer {
Task {
await connection.disconnect() // Unstructured! May not complete
}
}
return try await body(connection)
}
より良い例 — すべての経路で明示的にクリーンアップ:
func withConnection<R>(
_ body: (Connection) async throws -> R
) async throws -> R {
let connection = Connection()
try await connection.connect()
do {
let result = try await body(connection)
await connection.disconnect()
return result
} catch {
await connection.disconnect()
throw error
}
}
最善の例 — パターンを誤って使用できないようにする:
actor Client {
private let connection: Connection // Non-optional, always valid
private init(connection: Connection) {
self.connection = connection
}
static func withConnection<R>(
_ body: (Client) async throws -> R
) async throws -> R {
let connection = try await Connection.establish()
let client = Client(connection: connection)
do {
let result = try await body(client)
await connection.close()
return result
} catch {
await connection.close()
throw error
}
}
// No public connect()/disconnect() methods - can't forget cleanup
}
このパターンには次の利点があります。
- イニシャライザーをプライベートにし、ユーザーが必ず
withConnectionを経由するようにします - リソースをオプショナルではないプロパティとして保持するため、guard-let が不要です
- 関数が戻る前に必ずクリーンアップが行われます
notConnectedエラーケースを完全になくします
グローバルな可変状態を扱う Swift 6 の厳格な並行処理
Swift 6 の厳格な並行処理モードで stdout などの C グローバル変数にアクセスする場合は、ローカルな nonisolated(unsafe) 変数を使用してください。
@inline(__always)
private func flushStdout() {
nonisolated(unsafe) let out = stdout
fflush(out)
}
nonisolated(unsafe) アノテーションは、C グローバル変数へのこの特定のアクセスについて、並行処理のチェックを省略するようコンパイラーに指示します。
結果を待たないタスクには ThrowingDiscardingTaskGroup を使用する
複数のタスクを起動する必要があっても、それぞれの結果を収集する必要がない場合は、withThrowingDiscardingTaskGroup を使用してください。
try await withThrowingDiscardingTaskGroup { taskGroup in
taskGroup.addTask {
try await handleClient(client1)
}
taskGroup.addTask {
try await handleClient(client2)
}
}
グレースフルシャットダウンのパターン
長時間実行されるループでは、キャンセルとグレースフルシャットダウンの両方を確認してください。
while !Task.isShuttingDownGracefully && !Task.isCancelled {
try await handle(value)
}
// Use gracefulShutdown() for clean termination
try await gracefulShutdown()
AsyncStream:可能な限り使用を避ける
AsyncStream にはバックプレッシャーがありません。 プロデューサーがコンシューマーの処理より速く値を生成すると、値が上限のないバッファーに蓄積され、メモリが増加します。
代わりに、NIOAsyncSequence をマッピングする方法や、バックプレッシャーに対応したほかのシーケンスを優先してください。
// Bad - no backpressure, unbounded buffer
let stream = AsyncStream<Message> { continuation in
Task {
for await rawMessage in nioChannel.inbound {
continuation.yield(Message(rawMessage))
}
continuation.finish()
}
}
// Good - preserves backpressure from NIO
let messages = nioChannel.inbound.map { Message($0) }
for try await message in messages {
process(message)
}
AsyncStream を使わなければならない場合は、makeStream() を使用した構造化並行処理にしてください。
func run() async {
let (stream, continuation) = AsyncStream<Value>.makeStream()
async let producer: Void = runProducer(continuation: continuation)
for await value in stream {
process(value)
}
await producer
}
private func runProducer(
continuation: sending AsyncStream<Value>.Continuation
) async {
let cont = consume continuation // Transfer ownership
try? await withThrowingDiscardingTaskGroup { group in
group.addTask {
while !Task.isCancelled {
cont.yield(produceValue())
try await Task.sleep(for: .seconds(1))
}
}
}
cont.finish()
}
主なポイント:
sendingパラメーターは関数に所有権を移譲しますconsume continuationによって、タスクグループの子タスク間で安全に共有できますAsyncStream.ContinuationはSendableなので、nonisolated(unsafe)は不要ですAsyncStream.init内でTask { }を使用しないでください。これは構造化されていない並行処理です
検証チェックリスト(並行処理コードを変更した場合)
- 診断結果を解釈する前に、ビルド設定(デフォルトの分離、厳格な並行処理、今後導入予定の機能)を確認してください。
- リファクタリング後:
- テストを実行してください。特に並行処理の影響を受けやすいテストを実行してください(
references/testing.mdを参照)。 - パフォーマンスに関する変更の場合は、Instruments で検証してください(
references/performance.mdを参照)。 - ライフタイムに関する変更の場合は、
withスタイルの API を使用して、コードが適切に構造化されていることを検証してください。
- テストを実行してください。特に並行処理の影響を受けやすいテストを実行してください(
用語集
このスキル全体で使用される並行処理の基本用語については、references/glossary.md を参照してください。