agentsclimarketplace

Swift concurrency

Skill rusel95/ios-agent-skills/skills/swift-concurrency

Production-tested iOS Agent Skills for Claude Code, Codex, and 40+ AI coding tools. 8 enterprise-grade skills covering SwiftUI MVVM, UIKit MVVM, VIPER, TCA, Swift Concurrency, GCD, Testing, and Security Audit.

Install
npx -y skills add rusel95/ios-agent-skills --skill swift-concurrency

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

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.

SKILL.md

15.3 KB, as published. Nobody here has run it

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, TSan

Quick 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 context

Workflows

Workflow: Audit Existing Codebase

When: First encounter with a codebase, or preparing for Swift 6 migration.

  1. Check SWIFT_STRICT_CONCURRENCY setting (references/compiler-flags-ci.md)
  2. Scan for crash patterns: continuation misuse, cooperative pool blocking, TaskGroup without throttling (references/crash-patterns.md)
  3. Scan for @unchecked Sendable usage (references/sendable-transfer.md)
  4. Check actor isolation: reentrancy bugs, deinit access, split isolation (references/actor-isolation.md)
  5. Check AsyncStream usage: missing finish(), infinite sequences, no backpressure (references/asyncstream-memory.md)
  6. For security-sensitive code: token refresh, TOCTOU, Keychain (references/security-concurrency.md)
  7. Create refactoring/ directory with severity-ranked findings (references/refactoring-workflow.md)
  8. 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:

StepSettingWhat it does
1. TargetedSWIFT_STRICT_CONCURRENCY=targetedSurfaces 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. CompleteSWIFT_STRICT_CONCURRENCY=completeEnables 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 modeSWIFT_VERSION=6.0Promotes 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.

  1. Start with leaf modules (no internal dependencies) (references/migration-strategy.md)
  2. Enable targeted on the leaf first to surface obvious Sendable gaps
  3. Audit third-party SDKs for Sendable conformance; wrap blockers in actors (references/migration-strategy.md)
  4. Fix global mutable state: static var β†’ actor, Mutex, or let (references/actor-isolation.md)
  5. Advance to complete on the leaf, fix each warning module by module
  6. Annotate Sendable on public types; use sending where applicable (references/sendable-transfer.md)
  7. Check Swift 6.2 caller-side isolation changes (references/swift-6-2-changes.md)
  8. Only after complete is warning-free on the leaf, advance that leaf to Swift 6 language mode
  9. Enable per-target in SPM (references/compiler-flags-ci.md)
  10. Run TSan + LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 (references/testing-debugging.md)
  11. One PR per module, bottom-up order

Workflow: Create New Concurrent Feature

When: Building a new feature from scratch with Swift Concurrency.

  1. Choose isolation model using Decision Tree 1 (actor vs @MainActor vs Mutex)
  2. Choose concurrency structure using Decision Tree 2 (async let vs TaskGroup)
  3. Implement with Sendable-clean types (references/sendable-transfer.md)
  4. Add cancellation handling (references/advanced-patterns.md)
  5. Use Clock injection for testability (references/testing-debugging.md)
  6. Write deterministic tests with withMainSerialExecutor (references/testing-debugging.md)

Workflow: Fix Production Crash

When: Crash report points to continuation, actor, AsyncStream, or TaskGroup.

  1. Classify crash type (references/crash-patterns.md)
  2. Check if release-only (optimized builds strip cooperative pool assertions) -- Rule 9
  3. For reentrancy crashes: check state assumptions after await (references/actor-isolation.md)
  4. For security crashes: token refresh, TOCTOU patterns (references/security-concurrency.md)
  5. Add targeted test reproducing the crash (references/testing-debugging.md)
  6. 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:

  1. Never block the cooperative thread pool -- no semaphore.wait(), group.wait(), Thread.sleep(), synchronous file I/O in any async context
  2. Resume every continuation exactly once on every code path -- use withCheckedThrowingContinuation
  3. Re-check actor state after every await -- actors are reentrant at suspension points
  4. Use withDiscardingTaskGroup for fire-and-forget child tasks -- prevents result accumulation memory leaks
  5. AsyncStream must use onTermination for EVERY external resource cleanup β€” NotificationCenter observers, delegate assignments, timers, KVO, Combine subscribers, or any observer/callback registration made inside the stream's setup closure. deinit is not sufficient because an AsyncStream can outlive or be dropped independently of its owner; onTermination fires when the stream is finished or its task is cancelled, which is exactly when cleanup must run. Always pair every addObserver/delegate = self/registration call inside the stream setup with a symmetric removal in onTermination. Also call continuation.finish() from inside onTermination when your stream is an infinite observer pattern
  6. Mark public types with explicit Sendable conformance -- no automatic inference across module boundaries
  7. Limit TaskGroup child task count for unbounded work -- throttle to ProcessInfo.processInfo.activeProcessorCount
  8. Use @MainActor annotation for UI isolation, not MainActor.run {}
  9. Handle CancellationError silently -- never surface "cancelled" to users
  10. Inject Clock protocol for time-dependent code -- never hardcode Task.sleep
  11. Check Swift 6.2 caller-side isolation -- nonisolated async now inherits caller's actor; use @concurrent for explicit background
  12. 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:

  1. Sendable spiral: Conforming a type to Sendable cascades into 20+ errors across files. Temporarily use @preconcurrency import for the offending module and log a migration task in refactoring/discovered.md. Why: @preconcurrency suppresses Sendable checking at module boundaries while preserving local safety β€” it's the sanctioned escape hatch, unlike @unchecked Sendable which bypasses all checking.
  2. Actor isolation cascade: Adding @MainActor to a class cascades into dozens of async call-site errors. Start by marking individual methods @MainActor instead 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.
  3. 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.
  4. TaskGroup OOM: TaskGroup spawns thousands of child tasks and OOMs. Add a semaphore-based throttle: acquire before addTask, release inside the task. Limit to ProcessInfo.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 CI

References

Start here for most tasks: crash-patterns.md, actor-isolation.md, sendable-transfer.md. Then consult the specific reference based on your workflow.

ReferenceWhen to Read
references/rules.mdDo's and Don'ts quick reference: priority rules and critical anti-patterns
references/crash-patterns.mdProduction crash patterns: continuation misuse, cooperative pool deadlocks, TaskGroup OOM, watchdog kills, release-only crashes
references/actor-isolation.mdTask.init inherits isolation, compile-time isolation, nonisolated deinit, isolated deinit (Swift 6.1+), split isolation, assumeIsolated, actors as advanced tools
references/sendable-transfer.mdsending keyword, region-based isolation, @unchecked Sendable risks, public type inference, @preconcurrency scope
references/asyncstream-memory.mdInfinite sequence leaks, continuation.finish(), backpressure policies, withDiscardingTaskGroup, Task.detached stripping
references/swift-6-2-changes.mdApproachable Concurrency: caller isolation, @concurrent, MainActor default, @preconcurrency runtime crashes
references/migration-strategy.mdBottom-up module migration, third-party SDK blockers, global singleton conversion, actor hopping overhead
references/security-concurrency.mdToken refresh serialization, TOCTOU at await, Keychain serialization, sensitive data, actor double-spend
references/compiler-flags-ci.mdSWIFT_STRICT_CONCURRENCY, SPM per-target flags, Swift 6.2 feature flags, SwiftLint rules, CI pipeline
references/advanced-patterns.mdMutex vs actors, async let vs TaskGroup, isolated parameters, withTaskCancellationHandler, .task modifier, task-locals
references/diagnostics-fix-mapping.mdCompiler error β†’ fix mapping: "Sending risks data races", "non-sendable capture", "static property not safe", isolation errors
references/cancellation-patterns.mdCooperative cancellation, withTaskCancellationHandler, CancellationError handling, timeout patterns, SwiftUI .task
references/memory-retain-cycles.mdTask retain cycles, weak self patterns, async sequence retention, isolated deinit, testing for leaks
references/core-data-concurrency.mdNSManagedObject not Sendable, DAO pattern, NSManagedObjectID, actor-isolated context, @MainActor conflicts
references/testing-async.mdSwift Testing async, confirmation(), Clock injection, withMainSerialExecutor, TSan, deterministic tests
references/testing-debugging.mdwithMainSerialExecutor, Clock injection, TSan, Instruments, deterministic tests, timeouts
references/refactoring-workflow.mdrefactoring/ directory protocol, per-feature plans, severity ordering, PR sizing, verification checklist

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.