Key-value observation weird behavior

135 Views Asked by At

Perhaps this question is more general than I will make it seem, but I wanted to make sure I showed my full context in case something there is the cause of this issue.

  1. I wrote a singleton class with a KVC-compliant property and two methods:
class Singleton: NSObject {
    static let sharedInstance = Singleton()

    @objc dynamic var aProperty = false

    func updateDoesntWork() {
        aProperty = !aProperty
    }

    func updateDoesWork() {
        Singleton.sharedInstance.aProperty = !aProperty
    }
}
  1. I add an observer for the property in my app delegate's setup code:
Singleton.sharedInstance.addObserver(self, forKeyPath: #keyPath(Singleton.aProperty), options: [.new], context: nil)
  1. I override my app delegate's observeValue() method:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    NSLog("observeValue(forKeyPath: \(String(describing:keyPath)), of: \(String(describing:object)), change: \(String(describing:change)), context:\(String(describing:context)))")
}

Now, if I call Singleton.sharedInstance.updateDoesntWork(), I don't get a log entry for the change in aProperty. The property is changed (I verified this in the debugger), it's just that no notification is sent.

Whereas, if I call Singleton.sharedInstance.updateDoesWork(), everything works as I would expect -- the property is also changed, of course, but most importantly, this time the observer is notified of the change (the log entry is printed).

It makes no sense to me that I should need the full Singleton.sharedInstance.aProperty rather than just aProperty for KVO to work. What am I missing?

1

There are 1 best solutions below

0
E.Coms On

I assume you have trouble to use "var" for a singleton. You may consider use the following snippet to create a singleton and initializate some values including the observation exclusively used by the singleton:

class Singleton: NSObject {
    static private var sharedInstanceObserver : NSKeyValueObservation!

    static let sharedInstance: Singleton = {
        let sInstance = Singleton()
        sharedInstanceObserver = sInstance.observe(\Singleton.aProperty, options: .new) { st, value in
            print(st, value)
        }
        return sInstance
    }()

    @objc dynamic var aProperty = false

    func updateDoesntWork() {
        aProperty = !aProperty
    }

    func updateDoesWork() {
        Singleton.sharedInstance.aProperty = !aProperty
    }
}