Swift 6 / Strict Concurrency
When to invoke
- Starting a new Swift iOS / macOS App project and deciding the language mode.
- Writing
Package.swiftand settingswiftLanguageModes/SWIFT_STRICT_CONCURRENCY. - Adding a new third-party dependency and hitting concurrency warnings / errors.
- User asks "how do I configure Swift 6 / complete concurrency / Sendable / actor /
@preconcurrency".
Default decisions
- Swift 6 language mode + complete concurrency checking, applied from the first line of code.
- Default actor isolation is
MainActor, following the Xcode 26 App project template (SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor+SWIFT_APPROACHABLE_CONCURRENCY = YES; the SwiftPM equivalent isswiftSettings: [.defaultIsolation(MainActor.self)], which requires// swift-tools-version: 6.2). Under this default an unannotated in-house type never leaves the main actor, so it does not needSendableon its own account —Sendableis required only for a type that crosses intononisolatedcode, an actor, or a module with a different isolation default. Declare it explicitly when it does (struct Foo: Sendableorfinal class Foo: Sendable— the latter only valid when the class is non-inheritable and every stored property is bothSendableandlet; for classes with mutable state, preferactor Fooor@unchecked Sendablewith manual synchronisation). - Layer the isolation default per module (see
swiftpm-modularization): keepMainActoras the default for UI/App modules; Engine/Domain modules that are pure logic setswiftSettings: [.defaultIsolation(nil)]so they staynonisolatedby default and avoid needless main-actor hops. Work that must run off the main actor is marked explicitly with@concurrent(SE-0461) rather than left to inference. - Protocols that cross actor boundaries (DI injection points) are declared
Sendable, with methodsasync throws. Keep that requirement even when the implementation is an actor: the protocol'sSendableis not what fails when a non-Sendableframework type (AVAssetTrack,VNRequest) is involved. What fails is the value crossing the boundary, and the diagnostic names it:non-Sendable type 'X' cannot be returned from actor-isolated implementation to caller of protocol requirement. Dropping: Sendablefrom the protocol does not silence it — verified on Swift 6.3.2, the same error appears with and without. Two fixes that do work, both keeping the protocolSendable:@preconcurrency importthe framework whose type you don't own, or convert to aSendablevalue type at the actor boundary and never return the framework object itself (preferred — it makes the isolation real rather than asserted). - For third-party deps that don't support Swift 6 complete checking, in order of preference:
@preconcurrency import Xto isolate the import- Switch packages
- Defer adoption
Rationale
- A greenfield project has no legacy code to migrate, so the pain of complete checking gets amortised — solved once per actor / Sendable as you write — rather than as a one-shot big-bang migration later.
- Swift 6 is the long-term direction of the language; early adoption avoids technical debt.
- Complete checking catches data races at compile time, more than minimal / targeted can.
Deviation considerations
- Migrating existing Swift 5 code: switch to minimal or targeted concurrency checking and upgrade in stages; ramp up one file / module at a time, allowing
@preconcurrencyduring transition. - Significant third-party lag: if a critical dep doesn't support it, drop the whole project to targeted and patch module by module.
- Teaching / demo projects: if the goal is to demonstrate older API behaviour, keeping Swift 5 mode is fine.
Related skills
apple-platform-targets: the catalog toolchain is Xcode 26; Swift 6 language mode has been available since Xcode 16, so the language-mode choice does not constrain the deployment floor.swiftpm-modularization:swiftLanguageModes: [.v6]sets the default for the whole package; individual targets can opt down withswiftSettings: [.swiftLanguageMode(.v5)](available since swift-tools-version 6.0) — useful when migrating a legacy dependency without blocking the rest of the package.