| name | swift-concurrency |
| description | Diagnose data races, convert callback-based code to async/await, implement actor isolation patterns, resolve Sendable conformance issues, and guide Swift 6 migration. Use when developers mention: (1) Swift Concurrency, async/await, actors, or tasks, (2) "use Swift Concurrency" or "modern concurrency patterns", (3) migrating to Swift 6, (4) data races or thread safety issues, (5) refactoring closures to async/await, (6) @MainActor, Sendable, or actor isolation, (7) concurrent code architecture or performance optimization, (8) concurrency-related linter warnings (SwiftLint or similar; e.g. async_without_await, Sendable/actor isolation/MainActor lint). |
Swift Concurrency
Agent Rules
- Analyze
Package.swift or .pbxproj to determine Swift language mode (5.x vs 6) and toolchain before giving advice.
- Before proposing fixes, identify the isolation boundary:
@MainActor, custom actor, actor instance isolation, or nonisolated.
- Do not recommend
@MainActor as a blanket fix. Justify why main-actor isolation is correct for the code.
- Prefer structured concurrency (child tasks, task groups) over unstructured tasks. Use
Task.detached only with a clear reason.
- If recommending
@preconcurrency, @unchecked Sendable, or nonisolated(unsafe), require:
- a documented safety invariant
- a follow-up ticket to remove or migrate it
- For migration work, optimize for minimal blast radius (small, reviewable changes) and follow the validation loop: Build → Fix errors → Rebuild → Only proceed when clean.
- Course references are for deeper learning only. Use them sparingly and only when they clearly help answer the developer's question.
Triage Checklist (Before Advising)
- Capture the exact compiler diagnostics and the offending symbol(s).
- Identify the current isolation boundary and module defaults (
@MainActor, custom actor, default isolation).
- Confirm whether the code is UI-bound or intended to run off the main actor.
Quick Fix Mode (Use When)
Use Quick Fix Mode when:
- The errors are localized (single file or one type) and the isolation boundary is clear.
- The fix does not require API redesign or multi-module changes.
- You can explain the fix in 1–2 steps without changing behavior.
Skip Quick Fix Mode when:
- Default isolation or strict concurrency settings are unknown and likely affect behavior.
- The error crosses module boundaries or involves public API changes.
- The fix would require
@unchecked Sendable, @preconcurrency, or nonisolated(unsafe) without a clear invariant.
Project Settings Intake (Evaluate Before Advising)
Concurrency behavior depends on build settings. Before advising, determine these via Read on Package.swift or Grep in .pbxproj files:
| Setting | SwiftPM (Package.swift) | Xcode (.pbxproj) |
|---|
| Default isolation | .defaultIsolation(MainActor.self) | SWIFT_DEFAULT_ACTOR_ISOLATION |
| Strict concurrency | .enableExperimentalFeature("StrictConcurrency=targeted") | SWIFT_STRICT_CONCURRENCY |
| Upcoming features | .enableUpcomingFeature("NonisolatedNonsendingByDefault") | SWIFT_UPCOMING_FEATURE_* |
| Language mode | // swift-tools-version: at top | Swift Language Version build setting |
If any of these are unknown, ask the developer to confirm them before giving migration-sensitive guidance.
Smallest Safe Fixes (Quick Wins)
Prefer edits that preserve behavior while satisfying data-race safety.
- UI-bound types: isolate the type or specific members to
@MainActor (justify why UI-bound).
- Global/static mutable state: move into an
actor or isolate to @MainActor if UI-only.
- Background work: for work that should always hop off the caller’s isolation, move expensive work into an
async function marked @concurrent; for work that doesn’t touch isolated state but can inherit the caller’s isolation (for example with NonisolatedNonsendingByDefault), use nonisolated without @concurrent, or use an actor to guard mutable state.
- Sendable errors: prefer immutable/value types; avoid
@unchecked Sendable unless you can prove and document thread safety.
Quick Fix Playbook (Common Diagnostics -> Minimal Fix)
- "Main actor-isolated ... cannot be used from a nonisolated context"
- Quick fix: if UI-bound, make the caller
@MainActor or hop with await MainActor.run { ... }.
- Escalate if this is non-UI code or causes reentrancy; use
references/actors.md.
- "Actor-isolated type does not conform to protocol"
- Quick fix: add isolated conformance (e.g.,
extension Foo: @MainActor SomeProtocol).
- Escalate if the protocol requirements must be
nonisolated; use references/actors.md.
- "Sending value of non-Sendable type ... risks causing data races"
- Quick fix: confine access inside an actor or convert to a value type with immutable (
let) state.
- Escalate before
@unchecked Sendable; use references/sendable.md and references/threading.md.
- SwiftLint
async_without_await
- Quick fix: remove
async if not required; if required by protocol/override/@concurrent, use narrow suppression with rationale. See references/linting.md.
- "wait(...) is unavailable from asynchronous contexts" (XCTest)
- Quick fix: use
await fulfillment(of:) or Swift Testing equivalents. See references/testing.md.
Escalation Path (When Quick Fixes Aren't Enough)
- Gather project settings (default isolation, strict concurrency level, upcoming features).
- Re-evaluate isolation boundaries and which types cross them.
- Use the decision tree + references for the deeper fix.
- If behavior changes are possible, document the invariant and add tests/verification steps.
Quick Decision Tree
When a developer needs concurrency guidance, follow this decision tree:
-
Starting fresh with async code?
- Read
references/async-await-basics.md for foundational patterns
- For parallel operations →
references/tasks.md (async let, task groups)
-
Protecting shared mutable state?
- Need to protect class-based state →
references/actors.md (actors, @MainActor)
- Need thread-safe value passing →
references/sendable.md (Sendable conformance)
-
Managing async operations?
- Structured async work →
references/tasks.md (Task, child tasks, cancellation)
- Streaming data →
references/async-sequences.md (AsyncSequence, AsyncStream)
-
Working with legacy frameworks?
- Core Data integration →
references/core-data.md
- General migration →
references/migration.md
-
Performance or debugging issues?
- Slow async code →
references/performance.md (profiling, suspension points)
- Testing concerns →
references/testing.md (XCTest, Swift Testing)
-
Understanding threading behavior?
- Read
references/threading.md for thread/task relationship and isolation
-
Memory issues with tasks?
- Read
references/memory-management.md for retain cycle prevention
Triage-First Playbook (Common Errors -> Next Best Move)
- SwiftLint concurrency-related warnings
- Use
references/linting.md for rule intent and preferred fixes; avoid dummy awaits as “fixes”.
- SwiftLint
async_without_await warning
- Remove
async if not required; if required by protocol/override/@concurrent, prefer narrow suppression over adding fake awaits. See references/linting.md.
- "Sending value of non-Sendable type ... risks causing data races"
- First: identify where the value crosses an isolation boundary
- Then: use
references/sendable.md and references/threading.md (especially Swift 6.2 behavior changes)
- "Main actor-isolated ... cannot be used from a nonisolated context"
- First: decide if it truly belongs on
@MainActor
- Then: use
references/actors.md (global actors, nonisolated, isolated parameters) and references/threading.md (default isolation)
- "Class property 'current' is unavailable from asynchronous contexts" (Thread APIs)
- Use
references/threading.md to avoid thread-centric debugging and rely on isolation + Instruments
- "Actor-isolated type does not conform to protocol" (protocol conformance errors)
- First: determine whether the protocol requirements must execute on the actor (for example, UI work on
@MainActor) or can safely be nonisolated.
- Then: follow the Quick Fix Playbook entry for actor-isolated protocol conformance and
references/actors.md for implementation patterns (isolated conformances, nonisolated requirements, and escalation steps).
- XCTest async errors like "wait(...) is unavailable from asynchronous contexts"
- Use
references/testing.md (await fulfillment(of:) and Swift Testing patterns)
- Core Data concurrency warnings/errors
- Use
references/core-data.md (DAO/NSManagedObjectID, default isolation conflicts)
Core Patterns Reference
Concurrency Tool Selection
| Need | Tool | Key Guidance |
|---|
| Single async operation | async/await | Default choice for sequential async work |
| Fixed parallel operations | async let | Known count at compile time; auto-cancelled on throw |
| Dynamic parallel operations | withTaskGroup | Unknown count; structured — cancels children on scope exit |
| Sync → async bridge | Task { } | Inherits actor context; use Task.detached only with documented reason |
| Shared mutable state | actor | Prefer over locks/queues; keep isolated sections small |
| UI-bound state | @MainActor | Only for truly UI-related code; justify isolation |
Common Scenarios
Network request with UI update
Task { @concurrent in
let data = try await fetchData()
await MainActor.run { self.updateUI(with: data) }
}
Processing array items in parallel
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 Migration Quick Guide
Key changes in Swift 6:
- Strict concurrency checking enabled by default
- Complete data-race safety at compile time
- Sendable requirements enforced on boundaries
- Isolation checking for all async boundaries
Migration Validation Loop
Apply this cycle for each migration change:
- Build — Run
swift build or Xcode build to surface new diagnostics
- Fix — Address one category of error at a time (e.g., all Sendable issues first)
- Rebuild — Confirm the fix compiles cleanly before moving on
- Test — Run the test suite to catch regressions (
swift test or Cmd+U)
- Only proceed to the next file/module when all diagnostics are resolved
If a fix introduces new warnings, resolve them before continuing. Never batch multiple unrelated fixes — keep commits small and reviewable.
For detailed migration steps, see references/migration.md.
Reference Files
Load these files as needed for specific topics:
async-await-basics.md - async/await syntax, execution order, async let, URLSession patterns
tasks.md - Task lifecycle, cancellation, priorities, task groups, structured vs unstructured
threading.md - Thread/task relationship, suspension points, isolation domains, nonisolated
memory-management.md - Retain cycles in tasks, memory safety patterns
actors.md - Actor isolation, @MainActor, global actors, reentrancy, custom executors, Mutex
sendable.md - Sendable conformance, value/reference types, @unchecked, region isolation
linting.md - Concurrency-focused lint rules and SwiftLint async_without_await
async-sequences.md - AsyncSequence, AsyncStream, when to use vs regular async methods
core-data.md - NSManagedObject sendability, custom executors, isolation conflicts
performance.md - Profiling with Instruments, reducing suspension points, execution strategies
testing.md - XCTest async patterns, Swift Testing, concurrency testing utilities
migration.md - Swift 6 migration strategy, closure-to-async conversion, @preconcurrency, FRP migration
Verification Checklist (When You Change Concurrency Code)
- Confirm build settings (default isolation, strict concurrency, upcoming features) before interpreting diagnostics.
- Build — Verify the project compiles without new warnings or errors.
- Test — Run tests, especially concurrency-sensitive ones (see
references/testing.md).
- Performance — If performance-related, verify with Instruments (see
references/performance.md).
- Lifetime — If lifetime-related, verify deinit/cancellation behavior (see
references/memory-management.md).
- Check
Task.isCancelled in long-running operations.
- Never use semaphores or locks in async contexts — use actors or
Mutex instead.
Glossary
See references/glossary.md for quick definitions of core concurrency terms used across this skill.
Note: This skill is based on the comprehensive Swift Concurrency Course by Antoine van der Lee.