AvdLee / swift-concurrency
Install for your project team
Run this command in your project directory to install the skill for your entire team:
mkdir -p .claude/skills/swift-concurrency && curl -L -o skill.zip "https://fastmcp.me/Skills/Download/1254" && unzip -o skill.zip -d .claude/skills/swift-concurrency && rm skill.zip
Project Skills
This skill will be saved in .claude/skills/swift-concurrency/ and checked into git. All team members will have access to it automatically.
Important: Please verify the skill by reviewing its instructions before using it.
Expert guidance on Swift Concurrency best practices, patterns, and implementation. 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).
Skill Content
---
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
1. Analyze `Package.swift` or `.pbxproj` to determine Swift language mode (5.x vs 6) and toolchain before giving advice.
2. Before proposing fixes, identify the isolation boundary: `@MainActor`, custom actor, actor instance isolation, or nonisolated.
3. Do not recommend `@MainActor` as a blanket fix. Justify why main-actor isolation is correct for the code.
4. Prefer structured concurrency (child tasks, task groups) over unstructured tasks. Use `Task.detached` only with a clear reason.
5. If recommending `@preconcurrency`, `@unchecked Sendable`, or `nonisolated(unsafe)`, require:
- a documented safety invariant
- a follow-up ticket to remove or migrate it
6. For migration work, optimize for minimal blast radius (small, reviewable changes) and follow the validation loop: **Build → Fix errors → Rebuild → Only proceed when clean**.
7. 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)
1. Gather project settings (default isolation, strict concurrency level, upcoming features).
2. Re-evaluate isolation boundaries and which types cross them.
3. Use the decision tree + references for the deeper fix.
4. 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:
1. **Starting fresh with async code?**
- Read `references/async-await-basics.md` for foundational patterns
- For parallel operations → `references/tasks.md` (async let, task groups)
2. **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)
3. **Managing async operations?**
- Structured async work → `references/tasks.md` (Task, child tasks, cancellation)
- Streaming data → `references/async-sequences.md` (AsyncSequence, AsyncStream)
4. **Working with legacy frameworks?**
- Core Data integration → `references/core-data.md`
- General migration → `references/migration.md`
5. **Performance or debugging issues?**
- Slow async code → `references/performance.md` (profiling, suspension points)
- Testing concerns → `references/testing.md` (XCTest, Swift Testing)
6. **Understanding threading behavior?**
- Read `references/threading.md` for thread/task relationship and isolation
7. **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**
```swift
Task { @concurrent in
let data = try await fetchData()
await MainActor.run { self.updateUI(with: data) }
}
```
**Processing array items in parallel**
```swift
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:
1. **Build** — Run `swift build` or Xcode build to surface new diagnostics
2. **Fix** — Address one category of error at a time (e.g., all Sendable issues first)
3. **Rebuild** — Confirm the fix compiles cleanly before moving on
4. **Test** — Run the test suite to catch regressions (`swift test` or Cmd+U)
5. **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)
1. Confirm build settings (default isolation, strict concurrency, upcoming features) before interpreting diagnostics.
2. **Build** — Verify the project compiles without new warnings or errors.
3. **Test** — Run tests, especially concurrency-sensitive ones (see `references/testing.md`).
4. **Performance** — If performance-related, verify with Instruments (see `references/performance.md`).
5. **Lifetime** — If lifetime-related, verify deinit/cancellation behavior (see `references/memory-management.md`).
6. Check `Task.isCancelled` in long-running operations.
7. 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](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=skill-footer) by Antoine van der Lee.