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!
I haven’t yet watched that video [1] but, in general, I think it does make sense for your view models to be main-actor bound. However, you should keep in mind that there are two ways to achieve that in Xcode 26 beta:
- Apply
@MainActor
. - Set the Default Actor Isolation build setting to
MainActor
.
There are pros and cons to both approaches, but I suspect that a lot of folks will end up using the second approach for the main app and any libraries that are overwhelmingly UI-focused.
I believe that Sima is using the second approach, based on my searching the transcript for the word default.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Summer has been particularly glorious this year, so I’ve spent my lunchtimes riding rather than watching WWDC videos (-: