Why does the below code not create a compiler error?
I am mutating a @Published
property of this ObservableObject
on a background thread. Shouldn't the @MainActor
tag on this class mean that any code which mutates a published property must occur on the main thread?
import SwiftUI
@MainActor
class ViewModel: ObservableObject {
@Published private(set) var questionnaire: Questionnaire!
init() {
fetchQuestionnaire(id: "forest")
}
func fetchQuestionnaire(id: String) {
Task {
questionnaire = Questionnaire.testData
}
}
}
Or, is it actually OK to mutate @Published
properties on a background thread, and SwiftUI will perform the didSet
publisher on the main thread?
And if that’s the case, what even really is the point of @MainActor
in this situation?
When you isolate
ViewModel
to the main actor, its methods and properties are isolated to the main actor, too. In fact, this practice of isolating theObservableObject
to the main actor is actually recommended in WWDC 2021’s video Discover concurrency in SwiftUI. In that video, they recommend isolating theObservableObject
being observed by aView
to the main actor.And when you use
Task {…}
within an actor isolated method, that creates a new top level task on the current actor (i.e., the main actor). As Vadian pointed out, the documentation says (emphasis added):So, bottom line, you are not updating this on a “background thread”. You are updating it on the main actor. Hence, no compiler errors and no runtime warnings from the main thread checker.