Reputation: 655
SwiftUI 5 now uses the @Observable
wrapper to observe changes in a class instead of conforming it to ObservableObject
. This gets rid of @Published
wrappers which I actively use in my workflow:
Old implementation
class OldObservable: ObservableObject {
let model = SomeModel()
@Published var someValue: String?
init() {
setupSubscribers()
}
func setupSubscribers() {
// someValuePublisher could be PassthroughSubject<String?, Never>()
model.someValuePublisher.assign(to:$someValue)
}
}
New implementation
@Observable
class NewObservable {
let model = SomeModel()
var someValue: String?
init() {
setupSubscribers()
}
func setupSubscribers() {
// Not working anymore
model.someValuePublisher.assign(to:$someValue)
}
}
Since someValue is not a published property, I can't use the usual assign(to:)
anymore.
So far to make it work I have to use the old implementation assign(to:on) which I heard can lead to retain cycle issues and it's longer code for the same result.
var subscribers = Set<AnyCancellable>()
func setupSubscribers() {
model.someValuePublisher.assign(to: \.self.someValue, on: self).store(in: &subscribers)
}
I could also use the sink(ReceivedValue:)
but it also lengthen the code.
So is there a new and concise way to bind Combine publishers to the new Observable framework?
Upvotes: 15
Views: 1250
Reputation: 833
I found a way to solve the memory leak problem everywhere by simply overriding assign(to:)
:
// for publisher<XXX, Never>
extension Publisher where Failure == Never {
func assign<Root: AnyObject>(to keyPath: ReferenceWritableKeyPath<Root, Output>, on root: Root) -> AnyCancellable {
sink { [weak root] in
root?[keyPath: keyPath] = $0
}
}
}
// For publisher<XXX, Error>
extension Publisher where Failure == Error {
func assign<Root: AnyObject>(to keyPath: ReferenceWritableKeyPath<Root, Output>, on root: Root) -> AnyCancellable {
sink { [weak root] in
root?[keyPath: keyPath] = $0
}
}
}
Example:
var subscribers = Set<AnyCancellable>()
func setupSubscribers() {
// Same as before
model.someValuePublisher.assign(to: \.someValue, on: self).store(in: &subscribers)
}
reference:
Upvotes: 0