Swift Concurrency Audit
Scan your Swift codebase for common Swift 6 concurrency anti-patterns and violations that cause data races, crashes, and memory leaks.
What This Command Checks
- Missing @MainActor on UI Classes — View controllers and ObservableObjects without @MainActor
- Unsafe Task Self Capture — Tasks capturing self strongly without [weak self]
- Sendable Violations — Non-Sendable types crossing actor boundaries
- Improper Actor Isolation — Unsafe data access from actor contexts
- Missing Weak Self in Stored Tasks — Stored Task properties that leak memory
- Thread Confinement Violations — MainActor property access from background contexts
When to Use
Run this command when:
- Migrating to Swift 6 strict concurrency
- Seeing "data race" warnings or actor isolation errors
- Experiencing crashes with multithreaded access
- Before enabling strict concurrency checking
- After adding async/await or actors to your codebase
Concurrency Issues
🔴 Critical (Crashes/Memory Leaks)
Missing @MainActor on UI Classes
swift
// ❌ DATA RACE: Can be accessed from any thread
class ProfileViewModel: ObservableObject {
@Published var name: String = ""
}
// ✅ SAFE: MainActor ensures thread safety
@MainActor
class ProfileViewModel: ObservableObject {
@Published var name: String = ""
}Unsafe Task Self Capture
swift
// ❌ LEAKS: Task captures self strongly
var loadTask: Task<Void, Never>?
loadTask = Task {
self.data = await fetchData() // Retain cycle!
}
// ✅ SAFE: Use weak self
loadTask = Task { [weak self] in
guard let self = self else { return }
self.data = await fetchData()
}🟡 High Priority (Data Races)
Sendable Violations
swift
// ❌ DATA RACE: Non-Sendable type crossing actor boundary
class UserData {
var name: String
}
Task { @MainActor in
await actor.process(userData) // Warning: UserData is not Sendable
}
// ✅ SAFE: Make Sendable or use value types
struct UserData: Sendable {
let name: String
}Improper Actor Isolation
swift
// ❌ DATA RACE: Using actor data without isolation
let result = await dataActor.getData()
result.mutate() // Unsafe if result is mutable reference type!
// ✅ SAFE: Copy data before leaving actor context
let result = await dataActor.getData().copy()
result.mutate() // Safe - working with copy🟢 Medium Priority (Warnings)
Thread Confinement Violations
swift
// ❌ WARNING: MainActor property accessed in detached task
Task.detached {
print(self.mainActorProperty) // Warning!
}
// ✅ SAFE: Capture value before detaching
let value = mainActorProperty
Task.detached {
print(value) // Safe - captured before detaching
}Running the Audit
bash
# In Claude Code
/audit-concurrencyThe command will:
- Find all Swift files in your project
- Scan for the 6 concurrency patterns above
- Report findings with
file:linereferences - Prioritize by severity (Critical → Medium)
- Suggest fixes from swift-concurrency skill
Example Output
🔴 CRITICAL: Missing @MainActor (4 issues)
- ProfileViewModel.swift:12 - ObservableObject without @MainActor
- SettingsVC.swift:23 - UIViewController without @MainActor
Impact: Potential data race crashes
🔴 CRITICAL: Unsafe Task Self Capture (2 issues)
- NetworkManager.swift:45 - Task captures self without [weak self]
- ImageLoader.swift:67 - Stored task property leaks memory
Impact: Memory leaks, retain cycles
🟡 HIGH: Sendable Violations (3 issues)
- DataManager.swift:89 - Non-Sendable class crossing actor boundary
- UserSession.swift:34 - Mutable reference type in @Sendable closure
Impact: Data race warnings, potential crashes
🟢 MEDIUM: Thread Confinement (1 issue)
- BackgroundSync.swift:56 - MainActor property in detached task
Impact: Compiler warningsNext Steps
After running the audit:
- Fix Critical issues immediately — These cause crashes and memory leaks
- Address High priority issues — Enable strict concurrency checking
- Resolve Medium priority warnings — Clean build before shipping
For detailed fix patterns, use the swift-concurrency skill:
"How do I fix these Swift concurrency issues?"The skill provides copy-paste templates for:
- Pattern 1: Sendable types
- Pattern 2: Value capture before task
- Pattern 3: Weak self in tasks
- Pattern 4: Atomic snapshots
- And 8 more patterns for data persistence
Real-World Impact
Before audit
- 2-4 hours debugging mysterious crashes
- Data races discovered in production
- Actor isolation errors blocking Swift 6 migration
After audit
- 5-10 minutes to identify all concurrency issues
- Fix most issues with ~5 lines of code
- Smooth Swift 6 strict concurrency migration
Run this command periodically to catch concurrency regressions early.