Is there an alternative to Combine's @Published that signals a value change after it has taken place instead of before?
You can write your own custom property wrapper:
import Combine
@propertyWrapper
class DidSet<Value> {
private var val: Value
private let subject: CurrentValueSubject<Value, Never>
init(wrappedValue value: Value) {
val = value
subject = CurrentValueSubject(value)
wrappedValue = value
}
var wrappedValue: Value {
set {
val = newValue
subject.send(val)
}
get { val }
}
public var projectedValue: CurrentValueSubject<Value, Never> {
get { subject }
}
}
Before the introduction of ObservableObject
SwiftUI used to work the way that you specify - it would notify you after the change has been made. The change to willChange
was made intentionally and is probably caused by some optimizations, so using ObservableObjsect
with @Published
will always notify you before the changed by design. Of course you could decide not to use the @Published
property wrapper and implement the notifications yourself in a didChange
callback and send them via objectWillChange
property, but this would be against the convention and might cause issues with updating views. (https://developer.apple.com/documentation/combine/observableobject/3362556-objectwillchange) and it's done automatically when used with @Published
.
If you need the sink for something else than ui updates, then I would implement another publisher and not go agains the ObservableObject
convention.
Further to Eluss's good explanation, I'll add some code that works. You need to create your own PassthroughSubject
to make a publisher, and use the property observer didSet
to send changes after the change has taken place.
import Combine
class A {
public var fooDidChange = PassthroughSubject<Void, Never>()
var foo = false { didSet { fooDidChange.send() } }
}
let a = A()
let fooSink = a.fooDidChange.sink { _ in
print("foo is now \(a.foo)")
}
a.foo = true