swift-concurrency
Skill13 starsChanged 7 months agoMITView on GitHub
Use for any Swift Concurrency question on Apple platforms. Triggers on: actor reentrancy (stale state after await), AsyncStream lifecycle (continuation.finish, onTermination), TaskGroup memory and task throttling (activeProcessorCount), Sendable conformance across actor boundaries (non-Sendable third-party types), SWIFT_STRICT_CONCURRENCY=complete error triage ('Sending X risks causing data races'), Swift 6 / 6.2 migration (nonisolated caller-side isolation, @concurrent), cooperative pool blocking, async/await patterns, and continuation misuse. Also covers @MainActor isolation, structured concurrency, and converting callbacks to async/await. Use whenever the query mentions actor, AsyncStream, TaskGroup, Sendable, nonisolated, withCheckedContinuation, or Swift 6.
25 other projects use a version of this.
Adopted by
Most recently taken up by
- tornikegomareli/Skillstheir own version, first seen Nov 8, 2026
- uinaf/slopwaketheir own version, first seen Nov 3, 2026
- igorkulman/pi-skillstheir own version, first seen Nov 1, 2026
- VelimirMueller/claude-skillstheir own version, first seen Nov 1, 2026
- VelimirMueller/ai-development-skillstheir own version, first seen Nov 1, 2026
- The-Artificer-of-Ciphers-LLC/skills-from-the-artificertheir own version, first seen Nov 1, 2026
Used with
Projects with it also use swiftdata, swiftui-expert-skill and 9 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
Prisma-Labs-DevSkillswiftdata
API reference: SwiftData. Query for @Model, ModelContext, @Query, schema migrations.
Also in 3 projects that use this
xfstudioSkillswiftui-expert-skill
Write, review, or improve SwiftUI code following best practices for state management, view composition, performance, modern APIs, Swift concurrency, and iOS 26+ Liquid Glass adoption. Use when building new SwiftUI features, refactoring existing views, reviewing code quality, or adopting modern SwiftUI patterns.
Also in 8 projects that use this
pluginagentmarketplaceSkillswift-testing
Test Swift applications - XCTest, Swift Testing, UI tests, mocking, TDD, CI/CD
Also in 4 projects that use this
eugenepyvovarovSkillswift-concurrency-expert
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.
Also in 4 projects that use this
AvdLeeSkillswift-testing-expert
Expert guidance for Swift Testing: test structure, #expect/#require macros, traits and tags, parameterized tests, test plans, parallel execution, async waiting patterns, and XCTest migration. Use when writing new Swift tests, modernizing XCTest suites, debugging flaky tests, or improving test quality and maintainability in Apple-platform or Swift server projects.
Also in 3 projects that use this
twostrawsSkillswiftdata-pro
Writes, reviews, and improves SwiftData code using modern APIs and best practices. Use when reading, writing, or reviewing projects that use SwiftData.
Also in 3 projects that use this
Other versions25 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 25
- Provenance-Emu/Provenance★ 6.4ksame name
- pluk-inc/markdown-preview★ 2.8ksame name
- pluk-inc/md-preview.app★ 2.8ksame name
- AvdLee/Swift-Concurrency-Agent-Skill★ 1.7ksame name
- AFK-surf/OpenBridge★ 430same name
- Prisma-Labs-Dev/apple-skills★ 336same name
- jamesrochabrun/skills★ 209same name
- pepicrft/fuckingapproachableswiftconcurrency★ 156same name
- wendylabsinc/claude-skills★ 62same name
- nonameplum/agent-skills★ 17same name
15 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository11 other files it gives its agents.
- AGENTS.mdAGENTS.md
- gcd-operationsSkill
- ios-accessibilitySkill
- ios-localizationSkill
- ios-loggingSkill
- ios-securitySkill
- ios-testingSkill
- swiftui-mvvmSkill
- tca-swiftuiSkill
- uikit-mvvmSkill
- viper-uikitSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
21 rules. 0 are common, 4 are distinctive, and 17 are very specific to this project.
0
Common
Most of the 24 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
17
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 74
Check SWIFT_STRICT_CONCURRENCY setting (references/compiler-flags-ci.md)
- Very specific: line 77
Check actor isolation: reentrancy bugs, deinit access, split isolation (references/actor-isolation.md)
- Very specific: line 78
Check AsyncStream usage: missing finish(), infinite sequences, no backpressure (references/asyncstream-memory.md)
- Very specific: line 80
Create refactoring/ directory with severity-ranked findings (references/refactoring-workflow.md)
- Very specific: line 97
Start with leaf modules (no internal dependencies) (references/migration-strategy.md)
- Very specific: line 103
Check Swift 6.2 caller-side isolation changes (references/swift-6-2-changes.md)
- Distinctive: line 104
Only after complete is warning-free on the leaf, advance that leaf to Swift 6 language mode
- Very specific: line 106
Run TSan + LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 (references/testing-debugging.md)
- Very specific: line 116
Add cancellation handling (references/advanced-patterns.md)
- Very specific: line 117
Use Clock injection for testability (references/testing-debugging.md)
- Very specific: line 118
Write deterministic tests with withMainSerialExecutor (references/testing-debugging.md)
- Distinctive: line 125
Check if release-only (optimized builds strip cooperative pool assertions) -- Rule 9
- Very specific: line 128
Add targeted test reproducing the crash (references/testing-debugging.md)
- Very specific: line 129
Verify fix with TSan + LIBDISPATCH_COOPERATIVE_POOL_STRICT=1
- Very specific: line 136
Never block the cooperative thread pool -- no semaphore.wait(), group.wait(), Thread.sleep(), synchronous file I/O in any async context
- Very specific: line 139
Use withDiscardingTaskGroup for fire-and-forget child tasks -- prevents result accumulation memory leaks
- Very specific: line 143
Use @MainActor annotation for UI isolation, not MainActor.run {}
- Very specific: line 146
Check Swift 6.2 caller-side isolation -- nonisolated async now inherits caller's actor.
- Very specific: line 147
Before generating concurrent code, output a brief <thought> analyzing isolation domains, Sendable conformance, and potential reentrancy …
- Distinctive: line 153
When fixing concurrency issues, you may encounter cascading compiler errors.
- Distinctive: line 163
Before finalizing generated or refactored concurrent code, verify ALL:
“Common” is measured: kept by at least 6 in 10 of the 24 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Swift Concurrency
Enterprise-grade skill for Swift Concurrency crash prevention, strict concurrency migration, and Swift 6.2 readiness. Opinionated: prescribes structured concurrency over unstructured, actors for shared mutable state, @MainActor for UI isolation, Mutex for synchronous critical sections, compile-time isolation over runtime hops, and withCheckedContinuation over withUnsafeContinuation. Every rule in this skill has broken a real production app.
Concurrency Layers
Application Layer -> Structured concurrency: TaskGroup, async let, .task modifier
Actor Layer -> actor for shared mutable state, @MainActor for UI state
Async/Await Layer -> Cooperative, non-blocking async functions on the cooperative pool
Sendable Layer -> Compile-time data-race safety at isolation boundary crossings
Compiler Layer -> SWIFT_STRICT_CONCURRENCY=complete, Swift 6 language mode, TSanQuick Decision Trees
"What isolation does this type need?"
Does this type own mutable state shared across concurrency domains?
+-- YES -> Does it need to update UI?
| +-- YES -> @MainActor class/struct
| +-- NO -> Is the state complex or involves await points?
| +-- YES -> actor
| +-- NO -> Mutex<State> (Swift 6+, iOS 18+) or NSLock wrapper
+-- NO -> Is it a value type with only Sendable stored properties?
+-- YES -> Implicitly Sendable (internal types only; public needs explicit conformance)
+-- NO -> Can it be redesigned as a value type?
+-- YES -> Refactor to struct/enum
+-- NO -> Keep non-Sendable, contain within single isolation domain"async let vs TaskGroup vs Task {}?"
How many concurrent operations?
+-- Known at compile time (2-5) -> async let (simplest, heterogeneous return types OK)
+-- Dynamic count (N items) -> TaskGroup
| +-- Need results?
| | +-- YES -> withThrowingTaskGroup (iterate with for await)
| | +-- NO -> withDiscardingTaskGroup (no result accumulation leak)
| +-- Unbounded input? -> Throttle: limit addTask to activeProcessorCount
+-- Fire-and-forget from sync context?
+-- Need caller's isolation -> Task { } (inherits actor context)
+-- Need background, no isolation -> Task.detached { }
WARNING: detached strips priority, task-locals, cancellation propagation. Rarely correct."Is this safe to call from an async context?"
Does the API block the current thread?
+-- YES (semaphore.wait, group.wait, Thread.sleep, sync file I/O, NSLock in long section)
| -> NEVER in async context. Deadlocks cooperative pool (capped at CPU core count).
| -> Wrap in DispatchQueue + withCheckedContinuation to move off cooperative pool.
+-- NO -> Is the work CPU-bound and takes >1ms?
+-- YES -> Use @concurrent func (Swift 6.2+) or nonisolated func on dedicated queue
+-- NO -> Safe in async contextWorkflows
Workflow: Audit Existing Codebase
When: First encounter with a codebase, or preparing for Swift 6 migration.
- Check
SWIFT_STRICT_CONCURRENCYsetting (references/compiler-flags-ci.md) - Scan for crash patterns: continuation misuse, cooperative pool blocking, TaskGroup without throttling (
references/crash-patterns.md) - Scan for
@unchecked Sendableusage (references/sendable-transfer.md) - Check actor isolation: reentrancy bugs, deinit access, split isolation (
references/actor-isolation.md) - Check AsyncStream usage: missing
finish(), infinite sequences, no backpressure (references/asyncstream-memory.md) - For security-sensitive code: token refresh, TOCTOU, Keychain (
references/security-concurrency.md) - Create
refactoring/directory with severity-ranked findings (references/refactoring-workflow.md) - Execute fixes: 🔴 crash → 🟡 hang → 🟠 data race → 🟢 best practice
Workflow: Migrate Module to Strict Concurrency
When: Enabling SWIFT_STRICT_CONCURRENCY=complete or Swift 6 mode.
The migration is a THREE-STEP progression, not a binary flip. Do not jump directly to complete or to Swift 6 language mode — you will drown in errors. The canonical progression is:
| Step | Setting | What it does |
| 1. Targeted | SWIFT_STRICT_CONCURRENCY=targeted | Surfaces warnings only in code explicitly marked Sendable or in @preconcurrency boundaries. Low-noise entry point. Ship this first, fix the surfaced warnings, then advance. |
| 2. Complete | SWIFT_STRICT_CONCURRENCY=complete | Enables all concurrency checks as warnings. Work through each module until the warning count is zero. Still compiles and ships while you migrate. |
| 3. Swift 6 language mode | SWIFT_VERSION=6.0 | Promotes all concurrency checks from warnings to errors. Only switch here AFTER the module is clean under complete. This is the final gate. |
Most teams spend weeks in step 2 before advancing to step 3. Enabling step 3 prematurely produces hundreds of errors with no actionable incremental path.
- Start with leaf modules (no internal dependencies) (
references/migration-strategy.md) - Enable targeted on the leaf first to surface obvious Sendable gaps
- Audit third-party SDKs for Sendable conformance; wrap blockers in actors (
references/migration-strategy.md) - Fix global mutable state:
static var→ actor, Mutex, orlet(references/actor-isolation.md) - Advance to complete on the leaf, fix each warning module by module
- Annotate Sendable on public types; use
sendingwhere applicable (references/sendable-transfer.md) - Check Swift 6.2 caller-side isolation changes (
references/swift-6-2-changes.md) - Only after complete is warning-free on the leaf, advance that leaf to Swift 6 language mode
- Enable per-target in SPM (
references/compiler-flags-ci.md) - Run TSan +
LIBDISPATCH_COOPERATIVE_POOL_STRICT=1(references/testing-debugging.md) - One PR per module, bottom-up order
Workflow: Create New Concurrent Feature
When: Building a new feature from scratch with Swift Concurrency.
- Choose isolation model using Decision Tree 1 (actor vs @MainActor vs Mutex)
- Choose concurrency structure using Decision Tree 2 (async let vs TaskGroup)
- Implement with Sendable-clean types (
references/sendable-transfer.md) - Add cancellation handling (
references/advanced-patterns.md) - Use Clock injection for testability (
references/testing-debugging.md) - Write deterministic tests with
withMainSerialExecutor(references/testing-debugging.md)
Workflow: Fix Production Crash
When: Crash report points to continuation, actor, AsyncStream, or TaskGroup.
- Classify crash type (
references/crash-patterns.md) - Check if release-only (optimized builds strip cooperative pool assertions) -- Rule 9
- For reentrancy crashes: check state assumptions after await (
references/actor-isolation.md) - For security crashes: token refresh, TOCTOU patterns (
references/security-concurrency.md) - Add targeted test reproducing the crash (
references/testing-debugging.md) - Verify fix with TSan +
LIBDISPATCH_COOPERATIVE_POOL_STRICT=1
Code Generation Rules
<critical_rules> Whether reviewing, generating, or refactoring concurrent code, every output must be data-race-free, deadlock-free, and production-ready under Swift 6 strict concurrency. ALWAYS:
- Never block the cooperative thread pool -- no
semaphore.wait(),group.wait(),Thread.sleep(), synchronous file I/O in any async context - Resume every continuation exactly once on every code path -- use
withCheckedThrowingContinuation - Re-check actor state after every
await-- actors are reentrant at suspension points - Use
withDiscardingTaskGroupfor fire-and-forget child tasks -- prevents result accumulation memory leaks - AsyncStream must use
onTerminationfor EVERY external resource cleanup — NotificationCenter observers, delegate assignments, timers, KVO, Combine subscribers, or any observer/callback registration made inside the stream's setup closure.deinitis not sufficient because an AsyncStream can outlive or be dropped independently of its owner;onTerminationfires when the stream is finished or its task is cancelled, which is exactly when cleanup must run. Always pair everyaddObserver/delegate = self/registration call inside the stream setup with a symmetric removal inonTermination. Also callcontinuation.finish()from insideonTerminationwhen your stream is an infinite observer pattern - Mark public types with explicit
Sendableconformance -- no automatic inference across module boundaries - Limit TaskGroup child task count for unbounded work -- throttle to
ProcessInfo.processInfo.activeProcessorCount - Use
@MainActorannotation for UI isolation, notMainActor.run {} - Handle
CancellationErrorsilently -- never surface "cancelled" to users - Inject
Clockprotocol for time-dependent code -- never hardcodeTask.sleep - Check Swift 6.2 caller-side isolation --
nonisolated asyncnow inherits caller's actor; use@concurrentfor explicit background - Before generating concurrent code, output a brief
<thought>analyzing isolation domains, Sendable conformance, and potential reentrancy </critical_rules>
Fallback Strategies & Loop Breakers
<fallback_strategies> When fixing concurrency issues, you may encounter cascading compiler errors. If you fail to fix the same issue twice, break the loop:
- Sendable spiral: Conforming a type to Sendable cascades into 20+ errors across files. Temporarily use
@preconcurrency importfor the offending module and log a migration task inrefactoring/discovered.md. Why:@preconcurrencysuppresses Sendable checking at module boundaries while preserving local safety — it's the sanctioned escape hatch, unlike@unchecked Sendablewhich bypasses all checking. - Actor isolation cascade: Adding
@MainActorto a class cascades into dozens of async call-site errors. Start by marking individual methods@MainActorinstead of the whole class. Why: class-level isolation propagates to all methods, forcing every call site to be async — method-level isolation limits the blast radius. - Third-party SDK blocker: A third-party type is not Sendable and cannot be made so. Wrap it in a dedicated actor that owns the instance and mediates all access. Why: the actor serializes all access, making the non-Sendable type safe without needing to modify the SDK.
- TaskGroup OOM: TaskGroup spawns thousands of child tasks and OOMs. Add a semaphore-based throttle: acquire before
addTask, release inside the task. Limit toProcessInfo.processInfo.activeProcessorCount * 2. Why: each child task retains its result until the group iterates — unbounded tasks accumulate results in memory. </fallback_strategies>
Confidence Checks
Before finalizing generated or refactored concurrent code, verify ALL:
[] No cooperative pool blocking -- no semaphore.wait, Thread.sleep, sync I/O in any async function
[] No continuation leaks -- every withChecked*Continuation resumes on every path (success, failure, cancellation)
[] No unchecked Sendable -- every @unchecked Sendable has documented synchronization (Mutex, NSLock, or actor)
[] Actor reentrancy -- state re-checked after every await inside actors
[] AsyncStream cleanup -- finish() called in onTermination, no infinite sequence without cancellation
[] TaskGroup bounded -- child task count limited for unbounded input
[] MainActor correct -- UI state @MainActor-isolated, no MainActor.run anti-pattern
[] Swift 6.2 ready -- nonisolated async caller-isolation understood, @concurrent used where needed
[] Cancellation handled -- withTaskCancellationHandler for long-running ops, CancellationError caught silently
[] Tests deterministic -- Clock injected, withMainSerialExecutor used, no flaky timing dependencies
[] Compiler flags -- SWIFT_STRICT_CONCURRENCY=complete set, TSan enabled in CIReferences
Start here for most tasks:
crash-patterns.md,actor-isolation.md,sendable-transfer.md. Then consult the specific reference based on your workflow.
| Reference | When to Read |
references/rules.md | Do's and Don'ts quick reference: priority rules and critical anti-patterns |
references/crash-patterns.md | Production crash patterns: continuation misuse, cooperative pool deadlocks, TaskGroup OOM, watchdog kills, release-only crashes |
references/actor-isolation.md | Task.init inherits isolation, compile-time isolation, nonisolated deinit, isolated deinit (Swift 6.1+), split isolation, assumeIsolated, actors as advanced tools |
references/sendable-transfer.md | sending keyword, region-based isolation, @unchecked Sendable risks, public type inference, @preconcurrency scope |
references/asyncstream-memory.md | Infinite sequence leaks, continuation.finish(), backpressure policies, withDiscardingTaskGroup, Task.detached stripping |
references/swift-6-2-changes.md | Approachable Concurrency: caller isolation, @concurrent, MainActor default, @preconcurrency runtime crashes |
references/migration-strategy.md | Bottom-up module migration, third-party SDK blockers, global singleton conversion, actor hopping overhead |
references/security-concurrency.md | Token refresh serialization, TOCTOU at await, Keychain serialization, sensitive data, actor double-spend |
references/compiler-flags-ci.md | SWIFT_STRICT_CONCURRENCY, SPM per-target flags, Swift 6.2 feature flags, SwiftLint rules, CI pipeline |
references/advanced-patterns.md | Mutex vs actors, async let vs TaskGroup, isolated parameters, withTaskCancellationHandler, .task modifier, task-locals |
references/diagnostics-fix-mapping.md | Compiler error → fix mapping: "Sending risks data races", "non-sendable capture", "static property not safe", isolation errors |
references/cancellation-patterns.md | Cooperative cancellation, withTaskCancellationHandler, CancellationError handling, timeout patterns, SwiftUI .task |
references/memory-retain-cycles.md | Task retain cycles, weak self patterns, async sequence retention, isolated deinit, testing for leaks |
references/core-data-concurrency.md | NSManagedObject not Sendable, DAO pattern, NSManagedObjectID, actor-isolated context, @MainActor conflicts |
references/testing-async.md | Swift Testing async, confirmation(), Clock injection, withMainSerialExecutor, TSan, deterministic tests |
references/testing-debugging.md | withMainSerialExecutor, Clock injection, TSan, Instruments, deterministic tests, timeouts |
references/refactoring-workflow.md | refactoring/ directory protocol, per-feature plans, severity ordering, PR sizing, verification checklist |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

