
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
@MainActor
isolation for all declarations, ensuring that most code runs on the main thread. - Predictable
async
behavior, 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 async
methods 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
nonisolated
removes actor-based scheduling obligations for a method, allowing it to be invoked from any context without implicitawait
or thread hops.Sendable
is a protocol that marks types as safe for cross-actor or cross-thread transfer.- Enabling the flag
StrictConcurrency
enforces 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
: enforcesSendable
and 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
@MainActor
ensures UI safety. - Predictable async:
nonisolated(nonsending)
keeps async code on the calling actor. - Explicit parallelism:
@concurrent
enables 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.