$npx -y skills add johnrogers/claude-swift-engineering --skill modern-swiftUse when writing async/await code, enabling strict concurrency, fixing Sendable errors, migrating from completion handlers, managing shared state with actors, or using Task/TaskGroup for concurrency.
| 1 | # Modern Swift (6.2+) |
| 2 | |
| 3 | Swift 6.2 introduces strict compile-time concurrency checking with async/await, actors, and Sendable constraints that prevent data races at compile time instead of runtime. This is the foundation of safe concurrent Swift. |
| 4 | |
| 5 | ## Overview |
| 6 | |
| 7 | Modern Swift replaces older concurrency patterns (completion handlers, DispatchQueue, locks) with compiler-enforced safety. The core principle: if it compiles with strict concurrency enabled, it cannot have data races. |
| 8 | |
| 9 | ## Quick Reference |
| 10 | |
| 11 | | Need | Use | NOT | |
| 12 | |------|-----|-----| |
| 13 | | Async operation | `async/await` | Completion handlers | |
| 14 | | Main thread work | `@MainActor` | `DispatchQueue.main` | |
| 15 | | Shared mutable state | `actor` | Locks, serial queues | |
| 16 | | Parallel tasks | `TaskGroup` | `DispatchGroup` | |
| 17 | | Thread safety | `Sendable` | `@unchecked` everywhere | |
| 18 | |
| 19 | ## Core Workflow |
| 20 | |
| 21 | When writing async Swift code: |
| 22 | 1. Mark async functions with `async`, call with `await` |
| 23 | 2. Apply `@MainActor` to view models and UI-updating code |
| 24 | 3. Use `actor` instead of locks for shared mutable state |
| 25 | 4. Check `Task.isCancelled` or call `Task.checkCancellation()` in loops |
| 26 | 5. Enable strict concurrency in Package.swift for compile-time safety |
| 27 | |
| 28 | ## Reference Loading Guide |
| 29 | |
| 30 | **ALWAYS load reference files if there is even a small chance the content may be required.** It's better to have the context than to miss a pattern or make a mistake. |
| 31 | |
| 32 | | Reference | Load When | |
| 33 | |-----------|-----------| |
| 34 | | **[Concurrency Essentials](references/concurrency-essentials.md)** | Writing async code, converting completion handlers, using `await` | |
| 35 | | **[Swift 6 Concurrency](references/swift6-concurrency.md)** | Using `@concurrent`, `nonisolated(unsafe)`, or actor patterns | |
| 36 | | **[Task Groups](references/task-groups.md)** | Running multiple async operations in parallel | |
| 37 | | **[Task Cancellation](references/task-cancellation.md)** | Implementing long-running or cancellable operations | |
| 38 | | **[Strict Concurrency](references/strict-concurrency.md)** | Enabling Swift 6 strict mode or fixing Sendable errors | |
| 39 | | **[Macros](references/macros.md)** | Using or understanding Swift macros like `@Observable` | |
| 40 | | **[Modern Attributes](references/modern-attributes.md)** | Migrating legacy code or using `@preconcurrency`, `@backDeployed` | |
| 41 | | **[Migration Patterns](references/migration-patterns.md)** | Modernizing delegate patterns or UIKit views | |
| 42 | |
| 43 | ## Common Mistakes |
| 44 | |
| 45 | 1. **`@unchecked Sendable` as a quick fix** — Using `@unchecked Sendable` to silence compiler errors means you've opted out of safety. If the error persists after `@unchecked`, your code has a potential data race. Fix the underlying issue instead. |
| 46 | |
| 47 | 2. **Missing `await` at call sites** — Forgetting `await` when calling async functions is a compiler error, but checking `Task.isCancelled` in a loop without calling `Task.checkCancellation()` silently ignores cancellation. |
| 48 | |
| 49 | 3. **Capturing `self` in async blocks without `weak`** — Holding a strong reference to `self` in a long-running async task prevents deinit. Always use `[weak self]` in closures or use `.task` which auto-manages the lifecycle. |
| 50 | |
| 51 | 4. **Not checking task cancellation** — Long-running operations should regularly check `Task.isCancelled` or call `Task.checkCancellation()`, otherwise cancellation signals are ignored. |
| 52 | |
| 53 | 5. **Forgetting `@MainActor` on UI code and test suites** — Main test struct and view models that update `@Published` properties need `@MainActor`. Forgetting it silently allows cross-thread mutations. Apply `@MainActor` to: view models, view structs, main test structs, and any type that touches UI. |
| 54 | |
| 55 | 6. **Actor re-entrancy surprises** — `await` inside an actor method can release the lock temporarily. Another task may modify actor state. Design actor methods assuming state can change between `await` points. |