swift-concurrency-developer
Expert assistant for Swift concurrency patterns, including actors, isolation, and structured concurrency fixes.
Install
mkdir -p .claude/skills/swift-concurrency-developer && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/6699" && unzip -o skill.zip -d .claude/skills/swift-concurrency-developer && rm skill.zipInstalls to .claude/skills/swift-concurrency-developer
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Expert guidance on Swift concurrency using the Office Building mental model. Use when working with actors, isolation, Sendable, TaskGroups, or fixing concurrency warnings and data race issues.Key capabilities
- →Analyze Swift language mode and Xcode toolchain settings.
- →Identify isolation boundaries like @MainActor or custom actors.
- →Propose fixes for concurrency warnings and data race issues.
- →Recommend structured concurrency patterns over unstructured tasks.
- →Optimize migration work for minimal changes and add verification steps.
How it works
The skill analyzes Swift project settings to understand the language mode and toolchain, then identifies isolation boundaries before proposing fixes for concurrency issues.
Inputs & outputs
When to use swift-concurrency-developer
- →Debug data race issues
- →Implement actor isolation
- →Refactor to structured concurrency
- →Fix Sendable warnings
About this skill
Swift Concurrency Developer (Smart Router)
Purpose
Expert guidance on Swift's concurrency system using the "Office Building" mental model from Fucking Approachable Swift Concurrency, combined with comprehensive reference material from Swift Concurrency Course.
When Auto-Activated
- Working with actors, isolation, Sendable, TaskGroups
- Keywords:
actor,isolation,Sendable,TaskGroup,nonisolated,async let - Fixing concurrency warnings or data race issues
Agent Behavior Contract (Follow These Rules)
- Analyze the project/package file to find out which Swift language mode (Swift 5.x vs Swift 6) and which Xcode/Swift toolchain is used when advice depends on it.
- Before proposing fixes, identify the isolation boundary:
@MainActor, custom actor, actor instance isolation, or nonisolated. - Do not recommend
@MainActoras 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.detachedonly with a clear reason. - If recommending
@preconcurrency,@unchecked Sendable, ornonisolated(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 add verification steps.
- Course references are for deeper learning only. Use them sparingly and only when they clearly help answer the developer's question.
Project Settings Discovery
Always confirm these before interpreting diagnostics or giving migration-sensitive guidance. Do not guess — if any are unknown, ask the developer.
| Setting | SwiftPM (Package.swift) | Xcode (.pbxproj) |
|---|---|---|
| Language mode | .swiftLanguageMode(.v6) per-target inside swiftSettings (NOT package-level swiftLanguageVersions, which only advertises compatibility) | SWIFT_VERSION |
| Strict concurrency | .enableExperimentalFeature("StrictConcurrency=targeted") | SWIFT_STRICT_CONCURRENCY |
| Default isolation | .defaultIsolation(MainActor.self) | SWIFT_DEFAULT_ACTOR_ISOLATION |
| Upcoming features | .enableUpcomingFeature("NonisolatedNonsendingByDefault") | SWIFT_UPCOMING_FEATURE_* |
Tools: Read on Package.swift, Grep on .pbxproj.
Core Mental Model: The Office Building
Think of your app as an office building where isolation domains are private offices with locks:
| Concept | Office Analogy | Swift |
|---|---|---|
| MainActor | Front desk (handles all UI) | @MainActor |
| actor | Department offices (Accounting, Legal) | actor BankAccount { } |
| nonisolated | Hallways (shared space) | nonisolated func name() |
| Sendable | Photocopies (safe to share) | struct User: Sendable |
| Non-Sendable | Original documents (stay in one office) | class Counter { } |
Key insight: You can't barge into someone's office. You knock (await) and wait.
Quick Decision Tree
When a developer needs concurrency guidance:
-
Starting fresh with async code?
- Read
references/async-await-basics.mdfor foundational patterns - For parallel operations →
references/tasks.md(async let, task groups)
- Read
-
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)
- Need to protect class-based state →
-
Managing async operations?
- Structured async work →
references/tasks.md(Task, child tasks, cancellation) - Streaming data →
references/async-sequences.md(AsyncSequence, AsyncStream)
- Structured async work →
-
Working with legacy frameworks?
- Core Data integration →
references/core-data.md - General migration →
references/migration.md
- Core Data integration →
-
Performance or debugging issues?
- Slow async code →
references/performance.md(profiling, suspension points) - Testing concerns →
references/testing.md(XCTest, Swift Testing)
- Slow async code →
-
Understanding threading behavior?
- Read
references/threading.mdfor thread/task relationship and isolation
- Read
-
Memory issues with tasks?
- Read
references/memory-management.mdfor retain cycle prevention
- Read
Triage-First Playbook (Common Errors -> Next Best Move)
- SwiftLint concurrency-related warnings
- Use
references/linting.mdfor rule intent and preferred fixes; avoid dummy awaits as "fixes".
- Use
- "Sending value of non-Sendable type ... risks causing data races"
- First: identify where the value crosses an isolation boundary
- Then: use
references/sendable.mdandreferences/threading.md
- "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)
- First: decide if it truly belongs on
- XCTest async errors like "wait(...) is unavailable from asynchronous contexts"
- Use
references/testing.md(await fulfillment(of:)and Swift Testing patterns)
- Use
- Core Data concurrency warnings/errors
- Use
references/core-data.md(DAO/NSManagedObjectID, default isolation conflicts)
- Use
Quick Patterns
Async/Await
func fetchUser(id: Int) async throws -> User {
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
Parallel Work with async let
async let avatar = fetchImage("avatar.jpg")
async let banner = fetchImage("banner.jpg")
return Profile(avatar: try await avatar, banner: try await banner)
Tasks
// SwiftUI - cancels when view disappears
.task { avatar = await downloadAvatar() }
// Manual task (inherits actor context)
Task { await saveProfile() }
Task Entry Isolation (Swift 6.2)
Match a Task's entry isolation to its synchronous prefix — everything from { to the first await. Whatever runs in that prefix executes on the inherited actor.
- If the prefix needs
@MainActor(touches UI state, mutatesself.isLoading, etc.), keep the inherited start. - If the prefix has nothing main-actor-bound, prefer
Task { @concurrent in ... }and hop back viaMainActor.run { ... }only for the UI mutation. - A trivial non-main statement (e.g.
print) followed by main-actor work is not a reason to switch to@concurrent— the cheap line rides along. - For delayed retries, timers, and backoff: separate the waiting from the UI mutation. The sleep usually belongs off-main even when the final state update belongs on-main.
// ❌ Called from @MainActor; fetchData() is nonisolated, so the task starts on main then hops away
// (whether the hop happens depends on fetchData()'s declared isolation — nonisolated/@concurrent hop, @MainActor does not)
Task {
await fetchData() // nonisolated async
}
// ✅ Start off the main actor, hop back only for UI work
Task { @concurrent in
let data = try await fetchData()
await MainActor.run { self.items = data }
}
// ✅ Prefix DOES need main actor — keep inheritance
Task {
self.isLoading = true // needs @MainActor, before any await
await fetchData()
self.isLoading = false
}
TaskGroup for Dynamic Parallel Work
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { avatar = try await downloadAvatar() }
group.addTask { bio = try await fetchBio() }
try await group.waitForAll()
}
Actors
actor BankAccount {
var balance: Double = 0
func deposit(_ amount: Double) { balance += amount }
// No await needed - can access directly inside actor
nonisolated func bankName() -> String { "Acme Bank" }
}
await account.deposit(100) // Must await from outside
let name = account.bankName() // No await needed
Sendable Types
// Automatically Sendable - value type
struct User: Sendable {
let id: Int
let name: String
}
// Thread-safe class with internal synchronization
final class ThreadSafeCache: @unchecked Sendable {
private let lock = NSLock()
private var storage: [String: Data] = [:]
}
Common Mistakes
1. Thinking async = background
// WRONG: Still blocks main thread!
@MainActor func slowFunction() async {
let result = expensiveCalculation() // Synchronous = blocking
}
// CORRECT: Use detached task for CPU-heavy work
Task.detached(priority: .userInitiated) {
let result = expensiveCalculation()
await MainActor.run { updateUI(result) }
}
Production impact: Apps get rejected for "became unresponsive." See
references/production-pitfalls.mdsection 2.
2. Creating too many actors
Most things can live on MainActor. Only create actors when you have shared mutable state that can't be on MainActor.
3. Using MainActor.run unnecessarily
// WRONG
await MainActor.run { self.data = data }
// CORRECT - annotate the function
@MainActor func loadData() async { self.data = await fetchData() }
4. Blocking the cooperative thread pool (violates runtime contract)
Never use DispatchSemaphore, DispatchGroup.wait(), or condition variables in async code.
Why: These primitives hide dependencies from the runtime. The cooperative thread pool has a contract that threads will always make forward progress. Blocking primitives violate this contract and can cause deadlock.
// ❌ DANGEROUS: Can deadlock the cooperative pool
let semaphore = DispatchSemaphore(value: 0)
Task {
await doWork()
semaphore.signal()
}
semaphore.wait() // Thread blocked, runtime unaware
// ✅ Use async/await instead
let result = await doWork()
Debug tip: Set LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 to catch blocking calls during development.
5. Creating unnecessary Tasks
// WRONG - unstructured
Task { await fetchUsers() }
Task { await fetchPosts() }
// CORRECT - structured concurr
---
*Content truncated.*
Limitations
- →The skill does not recommend @MainActor as a blanket fix.
- →The skill requires a documented safety invariant for `@preconcurrency`, `@unchecked Sendable`, or `nonisolated(unsafe)`.
How it compares
This skill uses the "Office Building" mental model and specific rules to guide Swift concurrency fixes, unlike generic code analysis.
Compared to similar skills
swift-concurrency-developer side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| swift-concurrency-developer (this skill) | 1 | 3mo | No flags | Intermediate |
| swift-concurrency-expert | 4 | 5mo | No flags | Advanced |
| swift-concurrency | 4 | 2mo | No flags | Advanced |
| instruments-profiling | 3 | 2mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by anyproto
View all by anyproto →You might also like
swift-concurrency-expert
Dimillian
Swift Concurrency review and remediation for Swift 6.2+. Use when asked to review Swift Concurrency usage, improve concurrency compliance, or fix Swift concurrency compiler errors in a feature or file.
swift-concurrency
AvdLee
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).
instruments-profiling
steipete
Use when profiling native macOS or iOS apps with Instruments/xctrace. Covers correct binary selection, CLI arguments, exports, and common gotchas.
perf-optimizer
OneKeyHQ
Systematic performance optimization and regression debugging for OneKey mobile app (iOS). Use when: (1) Fixing performance regressions - when metrics like tokensStartMs, tokensSpanMs, or functionCallCount have regressed and need to be brought back to normal levels, (2) Improving baseline performance - when there's a need to optimize cold start time or reduce function call overhead, (3) User requests performance optimization/improvement/debugging for the app's startup or home screen refresh flow.
ios-debugger-agent
Dimillian
Use XcodeBuildMCP to build, run, launch, and debug the current iOS project on a booted simulator. Trigger when asked to run an iOS app, interact with the simulator UI, inspect on-screen state, capture logs/console output, or diagnose runtime behavior using XcodeBuildMCP tools.
ios-dev-guidelines
anyproto
Context-aware routing to Swift/iOS development patterns, architecture, and best practices. Use when working with .swift files, ViewModels, Coordinators, refactoring, or discussing Swift/SwiftUI patterns.