Published.Publisher
uses willSet
to emit values for the wrapped property. Unfortunately you cannot change this behaviour, the only solution is to implement your own property wrapper that uses didSet
instead of willSet
.
You can also customise whether you want to receive the current value when subscribing to the projectValue
publisher (matching the @Published
behaviour), using the emitCurrentValue
input argument. If it is set to true
, the current wrappedValue
is sent to new subscribers on subscription.
/// A type that publishes changes about its `wrappedValue` property _after_ the property has changed (using `didSet` semantics).
/// Reimplementation of `Combine.Published`, which uses `willSet` semantics.
@available(iOS 13, *)
@propertyWrapper
public class PostPublished<Value> {
/// A `Publisher` that emits the new value of `wrappedValue` _after it was_ mutated (using `didSet` semantics).
public let projectedValue: AnyPublisher<Value, Never>
/// A `Publisher` that fires whenever `wrappedValue` _was_ mutated. To access the new value of `wrappedValue`, access `wrappedValue` directly, this `Publisher` only signals a change, it doesn't contain the changed value.
public let valueDidChange: AnyPublisher<Void, Never>
private let didChangeSubject: any Subject<Value, Never>
public var wrappedValue: Value {
didSet {
didChangeSubject.send(wrappedValue)
}
}
/// - parameter emitCurrentValue: whether to emit the current wrapped value when subscribing to `projectValue`
public init(wrappedValue: Value, emitCurrentValue: Bool = false) {
self.wrappedValue = wrappedValue
let didChangeSubject: any Subject<Value, Never>
if emitCurrentValue {
didChangeSubject = CurrentValueSubject(wrappedValue)
} else {
didChangeSubject = PassthroughSubject<Value, Never>()
}
self.didChangeSubject = didChangeSubject
self.projectedValue = didChangeSubject.eraseToAnyPublisher()
self.valueDidChange = didChangeSubject.voidPublisher()
}
}
public extension Publisher {
/// Maps the `Output` of its upstream to `Void` and type erases its upstream to `AnyPublisher`.
func voidPublisher() -> AnyPublisher<Void, Failure> {
map { _ in Void() }
.eraseToAnyPublisher()
}
}
You can observe a @PostPublished
the same way you do a @Published
.
class UsingPostPublished {
@PostPublished var dontEmitInitial = 0
@PostPublished(emitCurrentValue: true) var emitInitial = 0
}
private var cancellables = Set<AnyCancellable>()
let usingPostPublished = UsingPostPublished()
usingPostPublished.$dontEmitInitial.sink {
print("dontEmitInitial did change to \($0)")
}.store(in: &cancellables)
usingPostPublished.$emitInitial.sink {
print("emitInitial did change to \($0)")
}.store(in: &cancellables)
usingPostPublished.emitInitial = 1
usingPostPublished.dontEmitInitial = 1
@Published
property callssend
inwillSet
. You would need to create your own publisher and callsend
indidSet
yourself instead of using@Published
. – Llano