I'm trying to understand a design pattern for accessing the isolated state held in an actor type from within a SwiftUI view.
Take this naive code:
actor Model: ObservableObject {
@Published var num: Int = 0
func updateNumber(_ newNum: Int) {
self.num = newNum
}
}
struct ContentView: View {
@StateObject var model = Model()
var body: some View {
Text("\(model.num)") // <-- Compiler error: Actor-isolated property 'num' can not be referenced from the main actor
Button("Update number") {
Task.detached() {
await model.updateNumber(1)
}
}
}
}
Understandably I get the compiler error Actor-isolated property 'num' can not be referenced from the main actor
when I try and access the isolated value. Yet I can't understand how to display this data in a view. I wonder if I need a ViewModel that observes the actor, and updates itself on the main thread, but get compile time error Actor-isolated property '$num' can not be referenced from a non-isolated context
.
class ViewModel: ObservableObject {
let model: Model
@Published var num: Int
let cancellable: AnyCancellable
init() {
let model = Model()
self.model = model
self.num = 0
self.cancellable = model.$num // <-- compile time error `Actor-isolated property '$num' can not be referenced from a non-isolated context`
.receive(on: DispatchQueue.main)
.sink { self.num = $0 }
}
}
Secondly, imagine if this code did compile, then I would get another error when clicking the button that the interface is not being updated on the main thread...again I'm not sure how to effect this from within the actor?
Standard practice here is to tie your model actor to the main actor. I don’t have time to look up the details right now but I’m pretty sure that one of the following WWDC talks has all the details:
-
WWDC 2021 Session 10019 Discover concurrency in SwiftUI
-
WWDC 2021 Session 10194 Swift concurrency: Update a sample app
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"