swift-concurrency — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited swift-concurrency (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Before proposing a fix:
Package.swift or .pbxproj to determine Swift language mode, strict concurrency level, default isolation, and upcoming features. Do this always, not only for migration work.@MainActor, custom actor, actor instance isolation, or nonisolated.@Observable is in play - it changes how isolation inference works on the type.Project settings that change concurrency behavior:
| Setting | SwiftPM (Package.swift) | Xcode (.pbxproj) |
|---|---|---|
| Language mode | swiftLanguageVersions or -swift-version (// swift-tools-version: is not a reliable proxy) | Swift Language Version |
| Strict concurrency | .enableExperimentalFeature("StrictConcurrency=targeted") | SWIFT_STRICT_CONCURRENCY |
| Default isolation | .defaultIsolation(MainActor.self) | SWIFT_DEFAULT_ACTOR_ISOLATION |
| Upcoming features | .enableUpcomingFeature("NonisolatedNonsendingByDefault") | SWIFT_UPCOMING_FEATURE_* |
If any of these are unknown, ask the developer to confirm them before giving migration-sensitive guidance. Do not guess.
Guardrails:
@MainActor as a blanket fix. Justify why the code is truly UI-bound.Task.detached only with a clear reason.@preconcurrency, @unchecked Sendable, or nonisolated(unsafe), require a documented safety invariant and a follow-up removal plan.@Observable is involved, understand that it does NOT automatically imply @MainActor isolation - the developer must explicitly opt in. Do not conflate the two.Use Quick Fix Mode when all of these are true:
Skip Quick Fix Mode when any of these are true:
| Diagnostic | First check | Smallest safe fix | Escalate to |
|---|---|---|---|
Main actor-isolated ... cannot be used from a nonisolated context | Is this truly UI-bound? | Isolate the caller to @MainActor or use await MainActor.run { ... } only when main-actor ownership is correct. | references/actors.md, references/threading.md |
Actor-isolated type does not conform to protocol | Must the requirement run on the actor? | Prefer isolated conformance (e.g., extension Foo: @MainActor SomeProtocol); use nonisolated only for truly nonisolated requirements. | references/actors.md |
Sending value of non-Sendable type ... risks causing data races | What isolation boundary is being crossed? | Keep access inside one actor, or convert the transferred value to an immutable/value type. | references/sendable.md, references/threading.md |
@Observable class with @MainActor property issues | Is the entire class UI-bound, or just some properties? | Apply @MainActor to the whole class if it's a view model; use nonisolated for properties that don't need main actor. | references/observable.md |
@Observable + async access from background | Are you mutating observed properties off the main actor? | Route mutations through await MainActor.run {} or isolate the class to @MainActor. | references/observable.md |
SwiftLint async_without_await | Is async actually required by protocol, override, or @concurrent? | Remove async, or use a narrow suppression with rationale. Never add fake awaits. | references/linting.md |
wait(...) is unavailable from asynchronous contexts | Is this legacy XCTest async waiting? | Replace with await fulfillment(of:) or Swift Testing equivalents. | references/testing.md |
| Core Data concurrency warnings | Are NSManagedObject instances crossing contexts or actors? | Pass NSManagedObjectID or map to a Sendable value type. | references/core-data.md |
Thread.current unavailable from asynchronous contexts | Are you debugging by thread instead of isolation? | Reason in terms of isolation and use Instruments/debugger instead. | references/threading.md |
| SwiftLint concurrency-related warnings | Which specific lint rule triggered? | Use references/linting.md for rule intent and preferred fixes; avoid dummy awaits. | references/linting.md |
Prefer changes that preserve behavior while satisfying data-race safety:
@MainActor.actor, or use @MainActor only if the state is UI-owned.async API marked @concurrent; when work can safely inherit caller isolation, use nonisolated without @concurrent.@unchecked Sendable.@MainActor when it drives UI; use nonisolated for computed properties or methods that don't touch UI state.This is one of the most common pain points in modern SwiftUI apps. The @Observable macro does NOT infer @MainActor - you must explicitly add it when the class drives UI.
// ✅ Correct: @MainActor view model with @Observable
@MainActor @Observable
final class ContentViewModel {
var items: [Item] = []
var isLoading = false
func loadItems() async {
isLoading = true
items = await APIClient.fetchItems()
isLoading = false
}
}Use @State (not @StateObject) and @Environment (not @EnvironmentObject) with @Observable:
// ✅ Correct Observation-era SwiftUI
struct ContentView: View {
@State private var viewModel = ContentViewModel()
var body: some View {
List(viewModel.items) { item in
Text(item.name)
}
.task { await viewModel.loadItems() }
}
}
// ✅ Passing via environment
@main
struct MyApp: App {
@State private var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environment(library)
}
}
}
struct LibraryView: View {
@Environment(Library.self) private var library
var body: some View {
List(library.books) { book in
BookView(book: book)
}
}
}For two-way bindings to @Observable objects, use @Bindable:
struct EditView: View {
@Bindable var viewModel: ContentViewModel
var body: some View {
TextField("Name", text: $viewModel.name)
}
}// ❌ Missing @MainActor - mutations from async contexts may happen off main thread
@Observable
final class ViewModel {
var items: [Item] = [] // SwiftUI reads this - must be main-actor-isolated
}
// ❌ Using old ObservableObject patterns with @Observable
@Observable
final class ViewModel: ObservableObject { // Don't conform to both
@Published var items: [Item] = [] // @Published is for ObservableObject
}
// ❌ Using @StateObject with @Observable
struct MyView: View {
@StateObject var vm = ViewModel() // Use @State instead
}For the full guide, see references/observable.md.
| Need | Tool | Key Guidance |
|---|---|---|
| Single async operation | async/await | Default choice for sequential async work |
| Fixed parallel operations | async let | Known count at compile time; auto-cancelled on throw |
| Dynamic parallel operations | withTaskGroup | Unknown count; structured - cancels children on scope exit |
| Sync → async bridge | Task { } | Inherits actor context; use Task.detached only with documented reason |
| Shared mutable state | actor | Prefer over locks/queues; keep isolated sections small |
| UI-bound state | @MainActor | Only for truly UI-related code; justify isolation |
| Observable view model | @MainActor @Observable | For SwiftUI-driving models; use @State not @StateObject |
Network request with UI update
Task { @concurrent in
let data = try await fetchData()
await MainActor.run { self.updateUI(with: data) }
}Processing array items in parallel
await withTaskGroup(of: ProcessedItem.self) { group in
for item in items {
group.addTask { await process(item) }
}
for await result in group {
results.append(result)
}
}Key changes in Swift 6:
Apply this cycle for each migration change:
swift build or Xcode build to surface new diagnosticsswift test or Cmd+U)If a fix introduces new warnings, resolve them before continuing. Never batch multiple unrelated fixes - keep commits small and reviewable.
For detailed migration steps, see references/migration.md.
Open the smallest reference that matches the question:
references/async-await-basics.md - async/await syntax, execution order, async let, URLSession patternsreferences/tasks.md - Task lifecycle, cancellation, priorities, task groups, structured vs unstructuredreferences/actors.md - Actor isolation, @MainActor, global actors, reentrancy, custom executors, Mutexreferences/sendable.md - Sendable conformance, value/reference types, @unchecked, region isolationreferences/threading.md - Execution model, suspension points, Swift 6.2 isolation behaviorreferences/observable.md - @Observable + @MainActor patterns, SwiftUI integration, async access, migration from ObservableObjectreferences/async-sequences.md - AsyncSequence, AsyncStream, when to use vs regular async methodsreferences/async-algorithms.md - Debounce, throttle, merge, combineLatest, channels, timersreferences/testing.md - Swift Testing first, XCTest fallback, leak checksreferences/performance.md - Profiling with Instruments, reducing suspension points, execution strategiesreferences/memory-management.md - Retain cycles in tasks, memory safety patternsreferences/core-data.md - NSManagedObject sendability, custom executors, isolation conflictsreferences/migration.md - Swift 6 migration strategy, closure-to-async conversion, @preconcurrency, FRP migrationreferences/linting.md - Concurrency-focused lint rules and SwiftLint async_without_awaitreferences/glossary.md - Quick definitions of core concurrency termsWhen changing concurrency code:
Task.isCancelled in long-running operations.Mutex would express ownership more safely.@Observable, verify that UI-driving properties are accessed on @MainActor.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.