Swift 6 mode turns data-race warnings into errors, so every project migrating to it hits these diagnostics.
In the Swift 6 language mode, the compiler proves your program free of data races: mutable state may only be touched from one isolation domain (an actor, the main actor, or a single task) at a time. Values crossing between domains must be safe to share, which the `Sendable` protocol expresses.
What is Sendable:
- Value types whose stored properties are all Sendable — implicitly for non-public structs and enums.
- Actors (they protect their own state).
finalclasses with only immutable (let) Sendable properties.- Closures marked
@Sendable, which may only capture Sendable values and can't mutate captured vars.
A plain class with var properties is not Sendable. Passing it into Task.detached, an actor method, or capturing it in a @Sendable closure gives errors like "passing argument of non-sendable type ... risks causing data races".
Fixes, in order of preference:
- Make the type a struct, or make the class
finalwithletproperties. - Isolate it: put it on
@MainActoror turn it into anactor. - Rely on region-based isolation: the compiler accepts a non-Sendable value passed across if it can prove the sender never uses it again.
- As a last resort,
@unchecked Sendablewhen you protect state yourself with a lock such asMutexfrom the Synchronization module.
Migrate module by module: enable strict concurrency checking (complete) in Swift 5 mode first, fix the warnings, then switch to swiftLanguageModes: [.v6].
Example
import Synchronization
struct Order: Sendable { let id: Int; var items: [String] } // value type: Sendable
final class Settings: Sendable { // immutable final class
let theme: String
init(theme: String) { self.theme = theme }
}
final class Counter: Sendable { // state guarded by a Mutex
private let value = Mutex(0)
func increment() -> Int {
value.withLock { v in v += 1; return v }
}
}
class Draft { var text = "" } // NOT Sendable
let counter = Counter()
await withTaskGroup(of: Void.self) { group in
for _ in 0..<100 { group.addTask { _ = counter.increment() } }
}
print(counter.increment()) // 101
let draft = Draft()
// Task.detached { draft.text = "x" } // Swift 6 error: non-Sendable 'draft' capturedRemember
- Sendable = safe to share across isolation domains
- Structs/enums of Sendable parts, actors and immutable final classes are Sendable
- Prefer restructuring or isolation over
@unchecked Sendable
Watch out for
- Silencing every error with
@unchecked Sendableornonisolated(unsafe), which just hides the races.