Suppose there's an actor like below
actor MyActor<State> {
private var state: State
private init(initialState: State) {
self.state = initialState
}
public func message(param: Int) {
// mutates state
...
}
}
Now I want to observe the isolated property state somehow, so that it can be used for example in SwiftUI, where it provides the view's state - or elsewhere.
For now, the @Observable macro from the Observation framework is out of question, since it does not support actors at this time.
Another idea would be to make the actor a Combine Publisher which publishes the state isolated property:
So, I would then change it like:
actor MyActor<State> {
private var _state: CurrentValueSubject<State, Never>
private init(initialState: State) {
self._state = .init(initialState)
}
public func message(param: Int) {
// mutates _state
...
}
}
and in order to make a publisher:
extension MyActor: Publisher {
public typealias Output = State
public typealias Failure = Never
public func receive<S>(
subscriber: S
) where S : Subscriber, Never == S.Failure, State == S.Input {
_state.receive(subscriber: subscriber)
}
}
However, the compiler complains (reasonable) with this error in function receive:
Actor-isolated instance method 'receive(subscriber:)' cannot be used
to satisfy nonisolated protocol requirement
One way to silence this error is for example:
extension MyActor: Publisher {
public typealias Output = State
public typealias Failure = Never
nonisolated
public func receive<S>(
subscriber: S
) where S : Subscriber, Never == S.Failure, State == S.Input {
// Note: probably should declare `_state` nonisolated
assumeIsolated { this in
this._state.receive(subscriber: subscriber)
}
}
}
Now, with some minor effort and applying a receive(on:) operator to dispatch the value delivery on the main thread, I could potentially integrate this actor into an @Observable type or a type conforming to ObservableObject and use this "model" as usual in SwiftUI.
However, it looks quite hackish and I'm unsure whether this is correct code regarding thread-safety.
Any other ideas to make an actor "observable" are welcome, too.
Maybe, the conclusion is, "Don't use actors, use @MainActor and final class"?
Yes. Remember, a final class marked
@MainActoris an actor.