Swift Concurrency Expert
Attribution: Sourced from steipete/agent-scripts by Peter Steinberger. Originally created by @Dimillian from Dimillian/Skills (2025-12-31).
Overview
Review and fix Swift Concurrency issues in Swift 6.2+ codebases by applying actor isolation, Sendable safety, and modern concurrency patterns with minimal behavior changes.
When to Use
- Fixing Swift 6 strict concurrency compiler errors
- Reviewing Sendable conformance, actor isolation, and data race safety
- Migrating pre-Swift 6 async code to strict concurrency
- Choosing between actors,
@MainActor,nonisolated, and@Sendable
Workflow
1. Triage the issue
- Capture the exact compiler diagnostics and the offending symbol(s).
- Identify the current actor context (
@MainActor,actor,nonisolated) and whether a default actor isolation mode is enabled. - Confirm whether the code is UI-bound or intended to run off the main actor.
2. Apply the smallest safe fix
Prefer edits that preserve existing behavior while satisfying data-race safety.
Common fixes:
- UI-bound types: annotate the type or relevant members with
@MainActor. - Protocol conformance on main actor types: make the conformance isolated (e.g.,
extension Foo: @MainActor SomeProtocol). - Global/static state: protect with
@MainActoror move into an actor. - Background work: move expensive work into a
@concurrentasync function on anonisolatedtype or use anactorto guard mutable state. - Sendable errors: prefer immutable/value types; add
Sendableconformance only when correct; avoid@unchecked Sendableunless you can prove thread safety.
Key Patterns
@MainActor annotation
// UI-bound type
@MainActor
class ViewModel: ObservableObject {
@Published var items: [Item] = []
}
// Protocol conformance
extension MyView: @MainActor SomeProtocol { ... }
Actor for shared mutable state
actor DataCache {
private var cache: [String: Data] = [:]
func store(_ data: Data, for key: String) {
cache[key] = data
}
func retrieve(for key: String) -> Data? {
cache[key]
}
}
Sendable conformance
// Immutable value type - safe
struct Config: Sendable {
let apiKey: String
let baseURL: URL
}
// Only use @unchecked when you can manually guarantee thread safety
final class ThreadSafeCache: @unchecked Sendable {
private let lock = NSLock()
private var cache: [String: Any] = [:]
}
Diagnostic Checklist
- Compiler diagnostic captured exactly
- Actor context identified (
@MainActor,actor,nonisolated, implicit) - Fix preserves existing behavior
- No
@unchecked Sendablewithout proven thread safety - No unnecessary
awaitorTask { }wrappers added