Swift Concurrency Resources:
Forums tags: Concurrency
The Swift Programming Language > Concurrency documentation
Migrating to Swift 6 documentation
WWDC 2022 Session 110351 Eliminate data races using Swift Concurrency — This ‘sailing on the sea of concurrency’ talk is a great introduction to the fundamentals.
WWDC 2021 Session 10134 Explore structured concurrency in Swift — The table that starts rolling out at around 25:45 is really helpful.
Swift Async Algorithms package
Swift Concurrency Proposal Index DevForum post
Why is flow control important? forums post
Matt Massicotte’s blog
Dispatch Resources:
Forums tags: Dispatch
Dispatch documentation — Note that the Swift API and C API, while generally aligned, are different in many details. Make sure you select the right language at the top of the page.
Dispatch man pages — While the standard Dispatch documentation is good, you can still find some great tidbits in the man pages. See Reading UNIX Manual Pages. Start by reading dispatch in section 3.
WWDC 2015 Session 718 Building Responsive and Efficient Apps with GCD [1]
WWDC 2017 Session 706 Modernizing Grand Central Dispatch Usage [1]
Avoid Dispatch Global Concurrent Queues forums post
Waiting for an Async Result in a Synchronous Function forums post
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] These videos may or may not be available from Apple. If not, the URL should help you locate other sources of this info.
Concurrency
RSS for tagConcurrency is the notion of multiple things happening at the same time.
Posts under Concurrency tag
145 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi,
I am programming in C and would like to use Grand Central Dispatch for parallel computing (I mostly do physics based simulations). I remember there used to be example codes provided by Apple, but can't find those now. Instead I get the plain documentation. May anyone point me to the correct resources? It will be greatly appreciated. Thanks ☺.
Hi, I've got this view model that will do a search using a database of keywords. It worked fine when the SearchEngine wasn't an actor but a regular class and the SearchResult wasn't a Sendable. But when I changed them, it returned Type of expression is ambiguous without a type annotation error at line 21 ( searchTask = Task {). What did I do wrong here? Thanks.
protocol SearchableEngine: Actor {
func searchOrSuggest(from query: String) -> SearchResult?
func setValidTitles(_ validTitles: [String])
}
@MainActor
final class SearchViewModel: ObservableObject {
@Published var showSuggestion: Bool = false
@Published var searchedTitles: [String] = []
@Published var suggestedKeyword: String? = nil
private var searchTask: Task<Void, Never>?
private let searchEngine: SearchableEngine
init(searchEngine: SearchableEngine) {
self.searchEngine = searchEngine
}
func search(_ text: String) {
searchTask?.cancel()
searchTask = Task {
guard !Task.isCancelled else { return }
let searchResult = await searchEngine.searchOrSuggest(from: text) ?? .notFound
guard !Task.isCancelled else { return }
await MainActor.run {
switch searchResult {
case let .searchItems(_, items):
showSuggestion = false
searchedTitles = items.map(\.title)
suggestedKeyword = nil
case let .suggestion(keyword, _, items):
showSuggestion = true
searchedTitles = items.map(\.title)
suggestedKeyword = keyword
case .notFound:
showSuggestion = false
searchedTitles = []
suggestedKeyword = nil
}
}
}
}
}
I have been working on an app for the past few months, and one issue that I have encountered a few times is an error where quick subsequent deletions cause issues with detached tasks that are triggered from some user actions.
Inside a Task.detached, I am building an isolated model context, querying for LineItems, then iterating over those items. The crash happens when accessing a Transaction property through a relationship.
var byTransactionId: [UUID: [LineItem]] {
return Dictionary(grouping: self) { item in
item.transaction?.id ?? UUID()
}
}
In this case, the transaction has been deleted, but the relationship existed when the fetch occurred, so the transaction value is non-nil. The crash occurs when accessing the id. This is the error.
SwiftData/BackingData.swift:1035: Fatal error: This model instance was invalidated because its backing data could no longer be found the store. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(backing: SwiftData.PersistentIdentifier.PersistentIdentifierBacking.managedObjectID(0xb43fea2c4bc3b3f5 <x-coredata://A9EFB8E3-CB47-48B2-A7C4-6EEA25D27E2E/Transaction/p1756>)))
I see other posts about this error and am exploring some suggestions, but if anyone has any thoughts, they would be appreciated.
I occasionally get this error in Xcode’s console:
Potential Structural Swift Concurrency Issue: unsafeForcedSync called from Swift Concurrent context.
What does this mean, and how can I resolve it? Googling it doesn’t turn up any results.
This doesn't crash the app - it’s just an error diagnostic that I see in the Xcode console. The app keeps running before and after the issue.
Is there a way I can set a breakpoint to catch this where it happens?
This comes up over and over, here on the forums and elsewhere, so I thought I’d post my take on it. If you have questions or comments, start a new thread here on the forums. Put it in the App & System Services > Processes & Concurrency subtopic and tag it with Concurrency.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Waiting for an Async Result in a Synchronous Function
On Apple platforms there is no good way for a synchronous function to wait on the result of an asynchronous function.
Lemme say that again, with emphasis…
On Apple platforms there is no good way for a synchronous function to wait on the result of an asynchronous function.
This post dives into the details of this reality.
Prime Offender
Imagine you have an asynchronous function and you want to call it from a synchronous function:
func someAsynchronous(input: Int, completionHandler: @escaping @Sendable (_ output: Int) -> Void) {
… processes `input` asynchronously …
… when its done, calls the completion handler with the result …
}
func mySynchronous(input: Int) -> Int {
… calls `someAsynchronous(…)` …
… waits for it to finish …
… results the result …
}
There’s no good way to achieve this goal on Apple platforms. Every approach you might try has fundamental problems.
A common approach is to do this working using a Dispatch semaphore:
func mySynchronous(input: Int) -> Int {
fatalError("DO NOT WRITE CODE LIKE THIS")
let sem = DispatchSemaphore(value: 0)
var result: Int? = nil
someAsynchronous(input: input) { output in
result = output
sem.signal()
}
sem.wait()
return result!
}
Note This code produces a warning in the Swift 5 language mode which turns into an error in the Swift 6 language mode. You can suppress that warning with, say, a Mutex. I didn’t do that here because I’m focused on a more fundamental issue here.
This code works, up to a point. But it has unavoidable problems, ones that don’t show up in a basic test but can show up in the real world. The two biggest ones are:
Priority inversion
Thread pools
I’ll cover each in turn.
Priority Inversion
Apple platforms have a mechanism that helps to prevent priority inversion by boosting the priority of a thread if it holds a resource that’s needed by a higher-priority thread. The code above defeats that mechanism because there’s no way for the system to know that the threads running the work started by someAsynchronous(…) are being waited on by the thread blocked in mySynchronous(…). So if that blocked thread has a high-priority, the system can’t boost the priority of the threads doing the work.
This problem usually manifests in your app failing to meet real-time goals. An obvious example of this is scrolling. If you call mySynchronous(…) from the main thread, it might end up waiting longer than it should, resulting in noticeable hitches in the scrolling.
Threads Pools
A synchronous function, like mySynchronous(…) in the example above, can be called by any thread. If the thread is part of a thread pool, it consumes a valuable resource — that is, a thread from the pool — for a long period of time. The raises the possibility of thread exhaustion, that is, where the pool runs out of threads.
There are two common thread pools on Apple platforms:
Dispatch
Swift concurrency
These respond to this issue in different ways, both of which can cause you problems.
Dispatch can choose to over-commit, that is, start a new worker thread to get work done while you’re hogging its existing worker threads. This causes two problems:
It can lead to thread explosion, where Dispatch starts dozens and dozens of threads, which all end up blocked. This is a huge waste of resources, notably memory.
Dispatch has an hard limit to how many worker threads it will create. If you cause it to over-commit too much, you’ll eventually hit that limit, putting you in the thread exhaustion state.
In contrast, Swift concurrency’s thread pool doesn’t over-commit. It typically has one thread per CPU core. If you block one of those threads in code like mySynchronous(…), you limit its ability to get work done. If you do it too much, you end up in the thread exhaustion state.
WARNING Thread exhaustion may seem like just a performance problem, but that’s not the case. It’s possible for thread exhaustion to lead to a deadlock, which blocks all thread pool work in your process forever.
There’s a trade-off here. Swift concurrency doesn’t over-commit, so it can’t suffer from thread explosion but is more likely deadlock, and vice versa for Dispatch.
Bargaining
Code like the mySynchronous(…) function shown above is fundamentally problematic. I hope that the above has got you past the denial stage of this analysis. Now let’s discuss your bargaining options (-:
Most folks don’t set out to write code like mySynchronous(…). Rather, they’re working on an existing codebase and they get to a point where they have to synchronously wait for an asynchronous result. At that point they have the choice of writing code like this or doing a major refactor.
For example, imagine you’re calling mySynchronous(…) from the main thread in order to update a view. You could go down the problematic path, or you could refactor your code so that:
The current value is always available to the main thread.
The asynchronous code updates that value in an observable way.
The main thread code responds to that notification by updating the view from the current value.
This refactoring may or may not be feasible given your product’s current architecture and timeline. And if that’s the case, you might end up deploying code like mySynchronous(…). All engineering is about trade-offs. However, don’t fool yourself into thinking that this code is correct. Rather, make a note to revisit this choice in the future.
Async to Async
Finally, I want to clarify that the above is about synchronous functions. If you have a Swift async function, there is a good path forward. For example:
func mySwiftAsync(input: Int) async -> Int {
let result = await withCheckedContinuation { continuation in
someAsynchronous(input: input) { output in
continuation.resume(returning: output)
}
}
return result
}
This looks like it’s blocking the current thread waiting for the result, but that’s not what happens under the covers. Rather, the Swift concurrency worker thread that calls mySwiftAsync(…) will return to the thread pool at the await. Later, when someAsynchronous(…) calls the completion handler and you resume the continuation, Swift will grab a worker thread from the pool to continue running mySwiftAsync(…).
This is absolutely normal and doesn’t cause the sorts of problems you see with mySynchronous(…).
IMPORTANT To keep things simple I didn’t implement cancellation in mySwiftAsync(…). In a real product it’s important to support cancellation in code like this. See the withTaskCancellationHandler(operation:onCancel:isolation:) function for the details.
I have two apps in which I have fixed warnings on Sendable, however there's one app where I did not and it looks like the rent's come due with Xcode 26.0, as I am getting over 100 warnings about Sendable. On a lark, I let the AI work on the warnings. There were so many that I ran out of free ChatGPT time and had to wait 24 hours. But today I cleared every remaining warning, but did the app still work? I figured I'd have to trash this code and do it by hand. But to my surprise, the app is working properly so far. More testing needs to be done and I need to dig into the code to make sure it's right, but so far, so good.
Hey all!
in my personal quest to make future proof apps moving to Swift 6, one of my app has a problem when setting an artwork image in MPNowPlayingInfoCenter
Here's what I'm using to set the metadata
func setMetadata(title: String? = nil, artist: String? = nil, artwork: String? = nil) async throws {
let defaultArtwork = UIImage(named: "logo")!
var nowPlayingInfo = [
MPMediaItemPropertyTitle: title ?? "***",
MPMediaItemPropertyArtist: artist ?? "***",
MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in
defaultArtwork
}
] as [String: Any]
if let artwork = artwork {
guard let url = URL(string: artwork) else { return }
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { return }
guard let image = UIImage(data: data) else { return }
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in
image
}
}
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
the app crashes when hitting
MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in
defaultArtwork
}
or
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in
image
}
commenting out these two make the app work again.
Again, no clue on why.
Thanks in advance
In iOS 26, should we have bootloader that runs the repo on startup - or should we have that inside tasks in root view?
we have repos that runs as a «closed» functions, we dont throw but updates swiftdata and we use @query in the views. So what is best?
and for the repo we should have a repo that runs the upserts manage relations eg? Should that run on a modelactor?
https://developer.apple.com/forums/thread/768776
Swift concurrency is an important part of my day-to-day job. I created the following document for an internal presentation, and I figured that it might be helpful for others.
If you have questions or comments, put them in a new thread here on DevForums. Use the App & System Services > Processes & Concurrency topic area and tag it with both Swift and Concurrency.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Swift Concurrency Proposal Index
This post summarises the Swift Evolution proposals that went into the Swift concurrency design. It covers the proposal that are implemented in Swift 6.2, plus a few additional ones that aren’t currently available.
The focus is here is the Swift Evolution proposals. For general information about Swift concurrency, see the documentation referenced by Concurrency Resources.
Swift 6.0
The following Swift Evolution proposals form the basis of the Swift 6.0 concurrency design.
SE-0176 Enforce Exclusive Access to Memory
link: SE-0176
notes: This defines the “Law of Exclusivity”, a critical foundation for both serial and concurrent code.
SE-0282 Clarify the Swift memory consistency model ⚛︎
link: SE-0282
notes: This defines Swift’s memory model, that is, the rules about what is and isn’t allowed when it comes to concurrent memory access.
SE-0296 Async/await
link: SE-0296
introduces: async functions, async, await
SE-0297 Concurrency Interoperability with Objective-C
link: SE-0297
notes: Specifies how Swift imports an Objective-C method with a completion handler as an async method. Explicitly allows @objc actors.
SE-0298 Async/Await: Sequences
link: SE-0298
introduces: AsyncSequence, for await syntax
notes: This just defines the AsyncSequence protocol. For one concrete implementation of that protocol, see SE-0314.
SE-0300 Continuations for interfacing async tasks with synchronous code
link: SE-0300
introduces: CheckedContinuation, UnsafeContinuation
notes: Use these to create an async function that wraps a legacy request-reply concurrency construct.
SE-0302 Sendable and @Sendable closures
link: SE-0302
introduces: Sendable, @Sendable closures, marker protocols
SE-0304 Structured concurrency
link: SE-0304
introduces: unstructured and structured concurrency, Task, cancellation, CancellationError, withTaskCancellationHandler(…), sleep(…), withTaskGroup(…), withThrowingTaskGroup(…)
notes: For the async let syntax, see SE-0317. For more ways to sleep, see SE-0329 and SE-0374. For discarding task groups, see SE-0381.
SE-0306 Actors
link: SE-0306
introduces: actor syntax
notes: For actor-isolated parameters and the nonisolated keyword, see SE-0313. For global actors, see SE-0316. For custom executors and the Actor protocol, see SE-0392.
SE-0311 Task Local Values
link: SE-0311
introduces: TaskLocal
SE-0313 Improved control over actor isolation
link: SE-0313
introduces: isolated parameters, nonisolated
SE-0314 AsyncStream and AsyncThrowingStream
link: SE-0314
introduces: AsyncStream, AsyncThrowingStream, onTermination
notes: These are super helpful when you need to publish a legacy notification construct as an async stream. For a simpler API to create a stream, see SE-0388.
SE-0316 Global actors
link: SE-0316
introduces: GlobalActor, MainActor
notes: This includes the @MainActor syntax for closures.
SE-0317 async let bindings
link: SE-0317
introduces: async let syntax
SE-0323 Asynchronous Main Semantics
link: SE-0323
SE-0327 On Actors and Initialization
link: SE-0327
notes: For a proposal to allow access to non-sendable isolated state in a deinitialiser, see SE-0371.
SE-0329 Clock, Instant, and Duration
link: SE-0329
introduces: Clock, InstantProtocol, DurationProtocol, Duration, ContinuousClock, SuspendingClock
notes: For another way to sleep, see SE-0374.
SE-0331 Remove Sendable conformance from unsafe pointer types
link: SE-0331
SE-0337 Incremental migration to concurrency checking
link: SE-0337
introduces: @preconcurrency, explicit unavailability of Sendable
notes: This introduces @preconcurrency on declarations, on imports, and on Sendable protocols. For @preconcurrency conformances, see SE-0423.
SE-0338 Clarify the Execution of Non-Actor-Isolated Async Functions
link: SE-0338
note: This change has caught a bunch of folks by surprise and there’s a discussion underway as to whether to adjust it.
SE-0340 Unavailable From Async Attribute
link: SE-0340
introduces: noasync availability kind
SE-0343 Concurrency in Top-level Code
link: SE-0343
notes: For how strict concurrency applies to global variables, see SE-0412.
SE-0374 Add sleep(for:) to Clock
link: SE-0374
notes: This builds on SE-0329.
SE-0381 DiscardingTaskGroups
link: SE-0381
introduces: DiscardingTaskGroup, ThrowingDiscardingTaskGroup
notes: Use this for task groups that can run indefinitely, for example, a network server.
SE-0388 Convenience Async[Throwing]Stream.makeStream methods
link: SE-0388
notes: This builds on SE-0314.
SE-0392 Custom Actor Executors
link: SE-0392
introduces: Actor protocol, Executor, SerialExecutor, ExecutorJob, assumeIsolated(…)
notes: For task executors, a closely related concept, see SE-0417. For custom isolation checking, see SE-0424.
SE-0395 Observation
link: SE-0395
introduces: Observation module, Observable
notes: While this isn’t directly related to concurrency, it’s relationship to Combine, which is an important exising concurrency construct, means I’ve included it in this list.
SE-0401 Remove Actor Isolation Inference caused by Property Wrappers
link: SE-0401, commentary
availability: upcoming feature flag: DisableOutwardActorInference
SE-0410 Low-Level Atomic Operations ⚛︎
link: SE-0410
introduces: Synchronization module, Atomic, AtomicLazyReference, WordPair
SE-0411 Isolated default value expressions
link: SE-0411, commentary
SE-0412 Strict concurrency for global variables
link: SE-0412
introduces: nonisolated(unsafe)
notes: While this is a proposal about globals, the introduction of nonisolated(unsafe) applies to “any form of storage”.
SE-0414 Region based Isolation
link: SE-0414, commentary
notes: To send parameters and results across isolation regions, see SE-0430.
SE-0417 Task Executor Preference
link: SE-0417, commentary
introduces: withTaskExecutorPreference(…), TaskExecutor, globalConcurrentExecutor
notes: This is closely related to the custom actor executors defined in SE-0392.
SE-0418 Inferring Sendable for methods and key path literals
link: SE-0418, commentary
availability: upcoming feature flag: InferSendableFromCaptures
notes: The methods part of this is for “partial and unapplied methods”.
SE-0420 Inheritance of actor isolation
link: SE-0420, commentary
introduces: #isolation, optional isolated parameters
notes: This is what makes it possible to iterate over an async stream in an isolated async function.
SE-0421 Generalize effect polymorphism for AsyncSequence and AsyncIteratorProtocol
link: SE-0421, commentary
notes: Previously AsyncSequence used an experimental mechanism to support throwing and non-throwing sequences. This moves it off that. Instead, it uses an extra Failure generic parameter and typed throws to achieve the same result. This allows it to finally support a primary associated type. Yay!
SE-0423 Dynamic actor isolation enforcement from non-strict-concurrency contexts
link: SE-0423, commentary
introduces: @preconcurrency conformance
notes: This adds a number of dynamic actor isolation checks (think assumeIsolated(…)) to close strict concurrency holes that arise when you interact with legacy code.
SE-0424 Custom isolation checking for SerialExecutor
link: SE-0424, commentary
introduces: checkIsolation()
notes: This extends the custom actor executors introduced in SE-0392 to support isolation checking.
SE-0430 sending parameter and result values
link: SE-0430, commentary
introduces: sending
notes: Adds the ability to send parameters and results between the isolation regions introduced by SE-0414.
SE-0431 @isolated(any) Function Types
link: SE-0431, commentary, commentary
introduces: @isolated(any) attribute on function types, isolation property of functions values
notes: This is laying the groundwork for SE-NNNN Closure isolation control. That, in turn, aims to bring the currently experimental @_inheritActorContext attribute into the language officially.
SE-0433 Synchronous Mutual Exclusion Lock 🔒
link: SE-0433
introduces: Mutex
SE-0434 Usability of global-actor-isolated types
link: SE-0434, commentary
availability: upcoming feature flag: GlobalActorIsolatedTypesUsability
notes: This loosen strict concurrency checking in a number of subtle ways.
Swift 6.1
Swift 6.1 has the following additions.
Vision: Improving the approachability of data-race safety
link: vision
SE-0442 Allow TaskGroup’s ChildTaskResult Type To Be Inferred
link: SE-0442, commentary
notes: This represents a small quality of life improvement for withTaskGroup(…) and withThrowingTaskGroup(…).
SE-0449 Allow nonisolated to prevent global actor inference
link: SE-0449, commentary
notes: This is a straightforward extension to the number of places you can apply nonisolated.
Swift 6.2
Xcode 26 beta has two new build settings:
Approachable Concurrency enables the following feature flags: DisableOutwardActorInference, GlobalActorIsolatedTypesUsability, InferIsolatedConformances, InferSendableFromCaptures, and NonisolatedNonsendingByDefault.
Default Actor Isolation controls SE-0466
Swift 6.2, still in beta, has the following additions.
SE-0371 Isolated synchronous deinit
link: SE-0371, commentary
introduces: isolated deinit
notes: Allows a deinitialiser to access non-sendable isolated state, lifting a restriction imposed by SE-0327.
SE-0457 Expose attosecond representation of Duration
link: SE-0457
introduces: attoseconds, init(attoseconds:)
SE-0461 Run nonisolated async functions on the caller’s actor by default
link: SE-0461
availability: upcoming feature flag: NonisolatedNonsendingByDefault
introduces: nonisolated(nonsending), @concurrent
notes: This represents a significant change to how Swift handles actor isolation by default, and introduces syntax to override that default.
SE-0462 Task Priority Escalation APIs
link: SE-0462
introduces: withTaskPriorityEscalationHandler(…)
notes: Code that uses structured concurrency benefits from priority boosts automatically. This proposal exposes APIs so that code using unstructured concurrency can do the same.
SE-0463 Import Objective-C completion handler parameters as @Sendable
link: SE-0463
notes: This is a welcome resolution to a source of much confusion.
SE-0466 Control default actor isolation inference
link: SE-0466, commentary
availability: not officially approved, but a de facto part of Swift 6.2
introduces: -default-isolation compiler flag
notes: This is a major component of the above-mentioned vision document.
SE-0468 Hashable conformance for Async(Throwing)Stream.Continuation
link: SE-0468
notes: This is an obvious benefit when you’re juggling a bunch of different async streams.
SE-0469 Task Naming
link: SE-0469
introduces: name, init(name:…)
SE-0470 Global-actor isolated conformances
link: SE-0470
availability: upcoming feature flag: InferIsolatedConformances
introduces: @SomeActor protocol conformance
notes: This is particularly useful when you want to conform an @MainActor type to Equatable, Hashable, and so on.
SE-0471 Improved Custom SerialExecutor isolation checking for Concurrency Runtime
link: SE-0471
notes: This is a welcome extension to SE-0424.
SE-0472 Starting tasks synchronously from caller context
link: SE-0472
introduces: immediate[Detached](…), addImmediateTask[UnlessCancelled](…),
notes: This introduces the concept of an immediate task, one that initially uses the calling execution context. This is one of those things where, when you need it, you really need it. But it’s hard to summary when you might need it, so you’ll just have to read the proposal (-:
In Progress
The proposals in this section didn’t make Swift 6.2.
SE-0406 Backpressure support for AsyncStream
link: SE-0406
availability: returned for revision
notes: Currently AsyncStream has very limited buffering options. This was a proposal to improve that. This feature is still very much needed, but the outlook for this proposal is hazy. My best guess is that something like this will land first in the Swift Async Algorithms package. See this thread.
SE-NNNN Closure isolation control
link: SE-NNNN
introduces: @inheritsIsolation
availability: not yet approved
notes: This aims to bring the currently experimental @_inheritActorContext attribute into the language officially. It’s not clear how this will play out given the changes in SE-0461.
Revision History
2025-09-02 Updated for the upcoming release Swift 6.2.
2025-04-07 Updated for the release of Swift 6.1, including a number of things that are still in progress.
2024-11-09 First post.
Hi,
In the WWDC25 session Elevate an app with Swift concurrency (timestamps: 8:04 and later), the StickerViewModel is shown annotated with @Observable but not @MainActor. The narration mentions that updates happen on the main thread, but that guarantee is left implicit in the calling code.
In Swift 6, though, one of the major benefits is stronger compiler enforcement against data races and isolation rules. If a view model were also annotated with @MainActor, then the compiler could enforce that observable state is only updated on the main actor, preventing accidental background mutations or updates that can cause data races between nonisolated and main actor-isolated uses.
Since @Observable already signals that state changes are intended to be observed (and in practice, usually by views), it seems natural that such types should also be main-actor isolated. Otherwise, we’re left with an implicit expectation that updates will always come from the main thread, but without the compiler’s help in enforcing that rule.
This also ties into the concept of local reasoning that was emphasized in other Swift 6 talks (e.g. Beyond the basics of structured concurrency). With @MainActor, I can look at a view model and immediately know that all of its state is main-actor isolated. With only @Observable, that guarantee is left out, which feels like it weakens the clarity that Swift 6 is trying to promote.
Would it be considered a best practice in Swift 6 to use both @Observable and @MainActor for UI-facing view models? Or is the intention that SwiftUI developers should rely on calling context to ensure main-thread updates, even if that means the compiler cannot enforce isolation?
Thanks!
Hi,
Are there rules around using Foundation Models:
In a background task/session?
Concurrently, i.e. a bunch simultaneously using Swift Concurrency?
I couldn't find this in the docs (sorry if I missed it) so wondering what's supported and what the best practice is here.
In case it matters, my primary platform is Vision Pro (so, M2).
I filed the following issue on swiftlang/swift on GitHub (Aug 8th), and a followup the swift.org forums, but not getting any replies. As we near the release of Swift 6.2, I want to know if what I'm seeing below is expected, or if it's another case where the compiler needs a fix.
protocol P1: Equatable { }
struct S1: P1 { }
// Error: Conformance of 'S1' to protocol 'P1' crosses into main actor-isolated code an can cause data races
struct S1Workaround: @MainActor P1 { } // OK
// Another potential workaround if `Equatable` conformance can be moved to the conforming type.
protocol P2 { }
struct S2: Equatable, P2 { } // OK
There was a prior compiler bug fix which addressed inhereted protocols regarding @MainActor. For Equatable, one still has to use @MainActoreven when the default actor isolation is MainActor.
Also affects Hashable and any other protocol inheriting from Equatable.
Hello,
While watching WWDC25: Code-along: Elevate an app with Swift concurrency at timestamp 25:48, I noticed something in the slide/diagram that might be incorrect.
The diagram shows ExtractSticker twice, but based on the code context and spoken explanation, I think it was meant to be ExtractSticker and ExtractColor.
Reasoning:
The surrounding code and narration describe the use of async let and a Sendable Data object.
From the flow, one task extracts a sticker while the other extracts a color, so it seems like the diagram is inconsistent.
I do understand that with @concurrent, having two ExtractSticker operations on the same Data is technically possible (with two concurrent process executing their respective ExtractSticker) — but that would be a different meaning than what the talk was describing.
Since concurrency is already a subtle and error-prone topic, I thought it was worth pointing this out. If I’m mistaken, I’d love clarification. Otherwise, this could be a small correction to keep things aligned and clearer for everyone.
Minor point overall, but Swift 6’s concurrency model is doing a fantastic job at helping us write safer code—so thank you to the team for that!
(Attaching screenshots for reference)
I have a UIKit app where I've adopted SwiftData and I'm struggling with a crash coming in from some of my users. I'm not able to reproduce it myself and as it only happens to a small fraction of my user base, it seems like a race condition of some sort.
This is the assertion message:
SwiftData/DefaultStore.swift:453: Fatal error: API Contract Violation: Editors must register their identifiers before invoking operations on this store SwiftData.DefaultStore: 00CF060A-291A-4E79-BEC3-E6A6B20F345E did not. (ID is unique per crash)
This is the ModelActor that crashes:
@available(iOS 17, *)
@ModelActor
actor ConsumptionDatabaseStorage: ConsumptionSessionStorage {
struct Error: LocalizedError {
var errorDescription: String?
}
private let sortDescriptor = [SortDescriptor(\SDConsumptionSession.startTimeUtc, order: .reverse)]
static func createStorage(userId: String) throws -> ConsumptionDatabaseStorage {
guard let appGroupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.defaultAppGroupIdentifier) else {
throw Error(errorDescription: "Invalid app group container ID")
}
func createModelContainer(databaseUrl: URL) throws -> ModelContainer {
return try ModelContainer(for: SDConsumptionSession.self, SDPriceSegment.self, configurations: ModelConfiguration(url: databaseUrl))
}
let databaseUrl = appGroupContainer.appendingPathComponent("\(userId).sqlite")
do {
return self.init(modelContainer: try createModelContainer(databaseUrl: databaseUrl))
} catch {
// Creating the model storage failed. Remove the database file and try again.
try? FileManager.default.removeItem(at: databaseUrl)
return self.init(modelContainer: try createModelContainer(databaseUrl: databaseUrl))
}
}
func isStorageEmpty() async -> Bool {
(try? self.modelContext.fetchCount(FetchDescriptor<SDConsumptionSession>())) ?? 0 == 0 // <-- Crash here!
}
func sessionsIn(interval: DateInterval) async throws -> [ConsumptionSession] {
let fetchDescriptor = FetchDescriptor(predicate: #Predicate<SDConsumptionSession> { sdSession in
if let startDate = sdSession.startTimeUtc {
return interval.start <= startDate && interval.end > startDate
} else {
return false
}
}, sortBy: self.sortDescriptor)
let consumptionSessions = try self.modelContext.fetch(fetchDescriptor) // <-- Crash here!
return consumptionSessions.map { ConsumptionSession(swiftDataSession: $0) }
}
func updateSessions(sessions: [ConsumptionSession]) async throws {
if #unavailable(iOS 18) {
// Price segments are duplicated if re-inserted so unfortunately we have to delete and reinsert sessions.
// On iOS 18, this is enforced by the #Unique macro on SDPriceSegment.
let sessionIds = Set(sessions.map(\.id))
try self.modelContext.delete(model: SDConsumptionSession.self, where: #Predicate<SDConsumptionSession> {
sessionIds.contains($0.id)
})
}
for session in sessions {
self.modelContext.insert(SDConsumptionSession(consumptionSession: session))
}
if self.modelContext.hasChanges {
try self.modelContext.save()
}
}
func deleteAllSessions() async {
if #available(iOS 18, *) {
try? self.modelContainer.erase()
} else {
self.modelContainer.deleteAllData()
}
}
}
The actor conforms to this protocol:
protocol ConsumptionSessionStorage {
func isStorageEmpty() async -> Bool
func hasCreditCardSessions() async -> Bool
func sessionsIn(interval: DateInterval) async throws -> [ConsumptionSession]
func updateSessions(sessions: [ConsumptionSession]) async throws
func deleteAllSessions() async
}
The crash is coming in from line 30 and 41, in other words, when trying to fetch data from the database. There doesn't seem to be any common trait for the crashes. They occur across iOS versions and device types.
Any idea what might cause this?
I have some code which handles doing some computation on a background thread before updating Core Data NSManagedObjects by using the NSManagedObjectContext.perform functions.
This code is covered in Sendable warnings in Xcode 26 (beta 6) because my NSManagedObject subclasses (autogenerated) are non-Sendable and NSManagedObjectContext.perform function takes a Sendable closure.
But I can't really figure out what I should be doing. I realize this pattern is non-ideal for Swift concurrency, but it's what Core Data demands AFAIK. How do I deal with this?
let moc = object.managedObjectContext!
try await moc.perform {
object.completed = true // Capture of 'object' with non-Sendable type 'MySpecialObject' in a '@Sendable' closure
try moc.save()
}
Thanks in advance for your help!
I'm working with SwiftData and SwiftUI and it's not clear to me if it is good practice to have a @ModelActor directly populate a SwiftUI view. For example when having to combine manual lab results and clinial results from HealthKit. The Clinical lab results are an async operation:
@ModelActor
actor LabResultsManager {
func fetchLabResultsWithHealthKit() async throws -> [LabResultDto] {
let manualEntries = try modelContext.fetch(FetchDescriptor<LabResult>())
let clinicalLabs = (try? await HealthKitService.getLabResults()) ?? []
return (manualEntries + clinicalLabs).sorted {
$0.date > $1.date
}.map {
return LabResultDto(from: $0)
}
}
}
struct ContentView: View {
@State private var labResults: [LabResultDto] = []
var body: some View {
List(labResults, id: \.id) { result in
VStack(alignment: .leading) {
Text(result.testName)
Text(result.date, style: .date)
}
}
.task {
do {
let labManager = LabResultsManager()
labResults = try await labManager.fetchLabResultsWithHealthKit()
} catch {
// Handle error
}
}
}
}
EDIT:
I have a few views that would want to use these labResults so I need an implementation that can be reused. Having to fetch and combine in each view will not be good practice. Can I pass a modelContext to a viewModel?
I have a TVTopShelfContentProvider that implements
func loadTopShelfContent() async -> (any TVTopShelfContent)?
When running on Xcode 26 b5 I am seeing the following error in swift 6 mode.
Non-Sendable type '(any TVTopShelfContent)?' cannot be returned from nonisolated override to caller of superclass instance method 'loadTopShelfContent()'
I'm not sure exactly what's changed here as it used to compile just fine but it's unclear now how I can work-around this error or how the API is supposed to be used.
The following definition is enough to trigger the error in Swift 6 language mode.
import TVServices
class ContentProvider: TVTopShelfContentProvider {
override func loadTopShelfContent() async -> (any TVTopShelfContent)? {
return nil
}
}
I can "fix" it by adding @preconcurrency to the TVServices import but it seems like this API is unusable currently? Or maybe it's user error on my part?
I'm struggling to convert Swift 5 to Swift 6.
As advised in doc, I first turned strict concurrency ON. I got no error.
Then, selected swift6… and problems pop up.
I have a UIViewController with
IBOutlets: eg a TextField.
computed var eg duree
func using UNNotification: func userNotificationCenter
I get the following error in the declaration line of the func userNotificationCenter:
Main actor-isolated instance method 'userNotificationCenter(_:didReceive:withCompletionHandler:)' cannot be used to satisfy nonisolated requirement from protocol 'UNUserNotificationCenterDelegate'
So, I declared the func as non isolated.
This func calls another func func2, which I had also to declare non isolated.
Then I get error on the computed var used in func2
Main actor-isolated property 'duree' can not be referenced from a nonisolated context
So I declared duree as nonsilated(unsafe).
Now comes the tricky part.
The computed var references the IBOutlet dureeField
if dureeField.text == "X"
leading to the error
Main actor-isolated property 'dureeField' can not be referenced from a nonisolated context
So I finally declared the class as mainActor and the textField as nonisolated
@IBOutlet nonisolated(unsafe) weak var dureeField : UITextField!
That silences the error (but declaring unsafe means I get no extra robustness with swift6) just to create a new one when calling dureeField.text:
Main actor-isolated property 'text' can not be referenced from a nonisolated context
Question: how to address properties inside IBOutlets ? I do not see how to declare them non isolated and having to do it on each property of each IBOutlet would be impracticable.
The following did work, but will make code very verbose:
if MainActor.assumeIsolated({dureeField.text == "X"}) {
So I must be missing something.
When attempting to compile an existing project with Swift 6, default isolation set to MainActor and approachable concurrency enabled, all awakeFromNib functions lead to the following compile error:
"Main actor-isolated instance method 'awakeFromNib()' has different actor isolation from nonisolated overridden declaration"
I've seen articles before approachable concurrency stating that one remedy is to wrap code within the function with MainActor.assumeIsolated{ }. However, that no longer addresses the error.
One combination of changes that removes the error is doing the following:
nonisolated override func awakeFromNib() {
super.awakeFromNib()
MainActor.assumeIsolated {
...
}
}
Honestly, that's a mess. Long term, we are looking to remove all these functions, but does anyone have a better solution?