Search code examples
swiftreactive-programmingcombinekey-value-observing

KVO publisher does not send signal on property change


I've just started learning Combine and am quite confused with behaviour of KVO publishers. They just do not publish any events except for the initial value. Here is the sample code I used:

@objc class SampleClass: NSObject {
    @objc var name: NSString = "1"
}

var a = SampleClass()

let kvoPublisher = a.publisher(for: \.name)
    .sink(receiveCompletion: {
        print("completion \($0)")
    }, receiveValue: { newVal in
        print("new val - \(newVal)")
    })

a.name = "2"
a.name = "3"

print("Finished; publisher = \(kvoPublisher) | a.name = \(a.name)")

The console output is

new val - 1
Finished; publisher = Combine.AnyCancellable | a.name = 3

Could you please explain what am I missing here and how to fix it?

Thanks.


Solution

  • You also need to mark the property as dynamic in order for it to be KVO compliant. publisher(for:) only works with KVO compliant properties, since it uses KVO under the hood.

    @objc class SampleClass: NSObject {
        @objc dynamic var name: NSString = "1"
    }
    

    Once you do that, the KVO publisher emits the updated values as expected.

    For more information on @objc vs @objc dynamic, see this Q&A.

    Bear in mind that you should only use KVO publishers when interacting with code that you cannot change. When you want to observe property values of types that you control, use @Published instead.