A Clear Path to Concurrency in Swift 6.2
Sprache: Dieser Artikel ist auf Englisch verfasst.
Swift 6.2 and Xcode 26 introduce a structured and intentional approach to concurrency designed to be safer, clearer, and easier to adopt. This article explains the key concurrency concepts introduced in Swift 6.2, including new compiler attributes, feature flags, and how to configure SwiftPM projects accordingly.
Approachable Concurrency
Approachable Concurrency is a build mode introduced in Xcode 26 and SwiftPM for Swift 6.2. It offers:
- Default
@MainActorisolation for all declarations, ensuring that most code runs on the main thread. - Predictable
asyncbehavior, without implicit thread jumps.
The WWDC talk Embracing Swift Concurrency presents a practical migration path from single-threaded to fully concurrent apps.
nonisolated(nonsending)
In Swift versions up to 6.1, nonisolated async methods were always detached to a global executor—even when invoked from @MainActor. This introduced unexpected thread switches.
With Swift 6.2, when using the NonisolatedNonsendingByDefault flag:
nonisolated asyncmethods inherit the actor of their caller.- If called on
@MainActor, they remain on the main thread. - This behavior is more intuitive and avoids unintended performance penalties.
class Service {
func fetchData() async { /* nonisolated(nonsending) by default */ }
}
@MainActor
func useService() async {
await Service().fetchData()
// Still on MainActor—no thread switch
}
@concurrent
To explicitly opt into parallel execution for methods, Swift 6.2 introduces the @concurrent attribute:
class Worker {
@concurrent
nonisolated func heavyJob() async {
// This runs in parallel on a background executor
}
}
@MainActor
func runWork() async {
await Worker().heavyJob()
// Resumes on MainActor after completion
}
@concurrent overrides nonisolated(nonsending) and ensures the method runs concurrently, suitable for CPU-intensive work.
Supporting Concepts: nonisolated, Sendable, and StrictConcurrency
nonisolatedremoves actor-based scheduling obligations for a method, allowing it to be invoked from any context without implicitawaitor thread hops.Sendableis a protocol that marks types as safe for cross-actor or cross-thread transfer.- Enabling the flag
StrictConcurrencyenforces compile-time checks forSendable, actor isolation, and concurrency rules.
These help define clear concurrency boundaries and enforce safety at compile time.
SwiftPM Configuration
To enable all of the above in a Swift package, use this SwiftPM configuration:
for target in package.targets {
var settings = target.swiftSettings ?? []
settings.append(.defaultIsolation(MainActor.self))
settings.append(.enableExperimentalFeature("StrictConcurrency"))
settings.append(.enableExperimentalFeature("NonisolatedNonsendingByDefault"))
settings.append(.enableExperimentalFeature("InferIsolatedConformances"))
target.swiftSettings = settings
}
Feature Summary
defaultIsolation(MainActor.self): sets default actor isolationStrictConcurrency: enforcesSendableand concurrency safetyNonisolatedNonsendingByDefault: appliesnonisolated(nonsending)behaviorInferIsolatedConformances: allows protocol conformances in actor-isolated contexts without global isolation
Thanks to Ole Begemann’s Gist for listing these flags via the Swift compiler’s hidden feature list.
How It All Works Together
- Single-threaded execution: default
@MainActorensures UI safety. - Predictable async:
nonisolated(nonsending)keeps async code on the calling actor. - Explicit parallelism:
@concurrentenables conscious offloading to background executors. - Strict safety:
Sendable, actors, and compiler enforcement viaStrictConcurrency. - Infer isolated conformances: allows isolated protocol implementations without broad isolation impact.
This provides a clear, staged concurrency model with control and visibility.