Swift concurrency
Read Package.swift and references/concurrency-checklist.md. Every target uses Swift 6 language mode with MainActor default isolation, InferIsolatedConformances, and NonisolatedNonsendingByDefault. Use this skill for every asynchronous service, observable model, stream, task group, actor, sendability, nonisolated, or @concurrent change.
Workflow
- Identify mutable state and assign one isolation domain.
- Keep UI-facing state on
@MainActor. - Make dependency protocols and cross-task values
Sendable. - Prefer structured
asyncfunctions and task groups to callbacks or detached work. - Define which scope owns each
Taskand when cancellation occurs. - Propagate cancellation; do not convert it into a user-facing failure accidentally.
- Prevent stale responses from overwriting newer state.
- Inject clocks or continuations for deterministic tests instead of sleeping.
- Run
make testandmake package-buildwith Swift 6.2; fix diagnostics at the boundary rather than weakening package settings or using broad suppressions.
Reject
DispatchQueue.main.asyncand global queues in new Swift concurrency code.Task.sleep(nanoseconds:); useTask.sleep(for:)or an injected clock.Task.detachedwithout a documented isolation/priority reason.- mutable shared state outside an actor/global actor/explicit synchronisation mechanism.
- broad
@unchecked Sendable; a narrow suppression requires a written thread-safety proof. - retaining unstructured tasks without cancellation or lifecycle ownership.
- swallowing errors or cancellation.
Review output
State the isolation of each mutable object, the task owner, cancellation path, and any value crossing an actor boundary. Prioritise data races and stale writes over stylistic concerns.