Hi all,
I'm trying to refactor my ObservableObject with publishers to @Observable objects.
Currently I actually use combine to observe between these ObservableObjects for some states usage. But I can't do so in @Observable objects.
So now I tried to ways to do so:
- Let @Observable class have a internal @ObservableObject as a publisher to publish certain properties for usage.
- Use AsyncStream to keep call withObservationTrack.
Here is my code:
@Observable
class MyObservalbeObject: @unchecked Sendable {
let publishers = MySubObject()
var name: String = "Frank"
var age: Int = 40
var nickname: String = ""
init() {
}
static let shared: MyObservalbeObject = {
return MyObservalbeObject()
}()
func setNickname(_ value: String) {
self.nickname = value
self.publishers.nickName = value
}
func observeName() -> AsyncStream<String> {
return AsyncStream { continuation in
internalTrackName(continuation)
// Handle stream termination to invalidate observations
continuation.onTermination = { _ in
continuation.finish()
print("observeName DONE.")
}
}
}
private func internalTrackName(_ continuation: AsyncStream<String>.Continuation) {
_ = withObservationTracking {
continuation.yield(self.name)
} onChange: {
Task {
await MainActor.run {
self.internalTrackName(continuation)
}
}
}
}
}
class MySubObject: ObservableObject, @unchecked Sendable {
@Published internal var nickName: String = ""
}
class MyModel: ObservableObject, @unchecked Sendable {
let myObject = MyObservalbeObject.shared
private var cancellable: AnyCancellable?
var task: Task<Void, Never>? = nil
init() {
trackNameFromObservedObjectProperty()
trackNameFromAsyncStream()
}
func changeName() {
myObject.name = "MyObject"
}
func trackNameFromObservedObjectProperty() {
cancellable = myObject.publishers.$nickName
.sink { [weak self] newName in
print("Updated name: \(newName)")
}
}
func trackNameFromAsyncStream() {
task = Task {
for await name in myObject.observeName() {
print("trackName3: \(name)")
}
}
Task {
await task?.value
task = nil
}
}
func cancelTracking() {
task?.cancel()
task = nil
}
}
I'm wondering it's the proper way to use in production?