---
name: ios-swift-concurrency
description: "async/await, actors, structured concurrency, task groups — modern Swift without the race conditions. Migrates code from completion handlers, picks the right actor isolation, handles cancellation correctly, and writes tests that don't flake."
license: MIT
---

# Swift Concurrency Pro

Swift Concurrency Pro is a coding assistant skill for iOS and macOS engineers moving real codebases onto async/await, actors, and structured concurrency. It focuses on the hard parts the compiler won't write for you: choosing the right isolation, eliminating data races flagged under strict concurrency checking, and converting legacy callback APIs without changing behavior. Aimed at developers shipping with Swift 5.9+ and preparing for the Swift 6 language mode.

## When to use it

- You flipped on `-strict-concurrency=complete` (or Swift 6 mode) and are drowning in "Sending value risks data races" and "non-Sendable type crossing actor boundary" errors.
- You have `completionHandler`-based code (URLSession, Core Data, delegate callbacks) you want to expose as `async` functions.
- A `@MainActor` class is forcing `await` hops everywhere and you need to figure out what actually belongs on the main actor.
- Your async tests pass locally but flake in CI.

## What it does

The skill reasons about isolation and rewrites accordingly. It wraps callbacks with `withCheckedThrowingContinuation` / `withCheckedContinuation` (and `withTaskCancellationHandler` when the underlying call is cancellable), and converts streams of callbacks into `AsyncStream` / `AsyncThrowingStream`. For parallel work it builds `withTaskGroup` / `withThrowingTaskGroup` rather than spraying detached `Task`s. It picks isolation deliberately — `@MainActor` for UI/observable state, a custom `actor` for shared mutable state, `nonisolated` for pure helpers — and applies `Sendable`, `@Sendable` closures, or `@unchecked Sendable` (with justification) where boundaries demand it. Cancellation is handled with `Task.checkCancellation()` and `Task.isCancelled` instead of being silently dropped. Tests are written against `async` XCTest methods or Swift Testing's `@Test`, using `await fulfillment(of:)` rather than arbitrary `sleep` calls.

## Example

```
curl -fsSL --create-dirs -o ~/.claude/skills/ios-swift-concurrency/SKILL.md https://claudeskil.com/skills/ios-swift-concurrency/SKILL.md

# then, in your project:
"Convert ImageLoader.load(url:completion:) to async/await and
 make the cache access race-free under Swift 6 mode."
```

Typical output replaces the completion handler with:

```swift
func load(url: URL) async throws -> UIImage {
    try await withCheckedThrowingContinuation { continuation in
        legacyLoad(url: url) { result in
            continuation.resume(with: result)
        }
    }
}
```

…and moves the cache into an `actor` so reads/writes are serialized.

## Tips & gotchas

- A continuation must be resumed exactly once. The skill audits every path so you don't leak a hung task or crash on a double-resume.
- `Task { }` inherits actor context; `Task.detached { }` does not — it flags places where detaching silently drops you off the main actor.
- `@MainActor` on a type isolates stored properties too, not just methods — overreaching it serializes more than you intend.

## FAQ

**Does it require Swift 6?** No. It works in Swift 5.9+ and helps you adopt strict concurrency incrementally, one module at a time.

**Will it use `@unchecked Sendable` to silence errors?** Only as a last resort, with a comment explaining the invariant — it prefers real fixes (actors, value types, isolation).

**Does it handle Combine?** It can bridge `Publisher` to `AsyncSequence` via `.values`, but its focus is native concurrency.
