Integrating actors with existing code doesn't really seem to be as simple as Apple wants you to believe. Consider the following simple actor:
actor Foo {
var value: Int = 0
}
Trying to access this property from any AppKit/UIKit (task-less) controller just can't work because every Task is asynchronous.
class AppKitController {
func myTaskLessFunc() {
let f = Foo()
var v: Int = -1
Task { v = await f.value }
}
}
This will give you the expected error Mutation of captured var 'v' in concurrently-executing code, also if you tried to lock this it wouldn't work. nonisolated actor methods also don't help, since you will run into the same problem.
So how do you read actor properties synchronous in a task-less context at all?
I found a way to do it by using Combine, like this:
import Combine
actor Foo {
nonisolated let valuePublisher = CurrentValueSubject<Int, Never>(0)
var value: Int = 0 {
didSet {
valuePublisher.value = value
}
}
}
By providing an non-isolated publisher, we can propagate the actor value safely to the outside, since Publishers are thread safe.
Callers can either access foo.valuePublisher.value or subscribe to it from the outside, without requiring an async context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With