How to use KVO for UserDefaults in Swift? How to use KVO for UserDefaults in Swift? ios ios

How to use KVO for UserDefaults in Swift?


As of iOS 11 + Swift 4, the recommended way (according to SwiftLint) is using the block-based KVO API.

Example:

Let's say I have an integer value stored in my user defaults and it's called greetingsCount.

First I need to extend UserDefaults with a dynamic var that has the same name as the user defaults key you want to observe:

extension UserDefaults {    @objc dynamic var greetingsCount: Int {        return integer(forKey: "greetingsCount")    }}

This allows us to later on define the key path for observing, like this:

var observer: NSKeyValueObservation?init() {    observer = UserDefaults.standard.observe(\.greetingsCount, options: [.initial, .new], changeHandler: { (defaults, change) in        // your change logic here    })}

And never forget to clean up:

deinit {    observer?.invalidate()}


From the blog of David Smithhttp://dscoder.com/defaults.htmlhttps://twitter.com/catfish_man/status/674727133017587712

If one process sets a shared default, then notifies another process toread it, then you may be in one of the very few remaining situationsthat it's useful to call the -synchronize method in: -synchronize actsas a "barrier", in that it provides a guarantee that once it hasreturned, any other process that reads that default will see the newvalue rather than the old value.

For applications running on iOS 9.3and later / macOS Sierra and later, -synchronize is not needed (orrecommended) even in this situation, since Key-Value Observation ofdefaults works between processes now, so the reading process can justwatch directly for the value to change. As a result of that,applications running on those operating systems should generally nevercall synchronize.

So in most likely case you do not need to set to call synchronize. It is automatically handled by KVO.

To do this you need add observer in your classes where you are handling persistanceServiceValueChangedNotification notification. Let say you are setting a key with name "myKey"

Add observer in your class may be viewDidLoad etc

 UserDefaults.standard.addObserver(self, forKeyPath: "myKey", options: NSKeyValueObservingOptions.new, context: nil)

Handle the observer

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {    //do your changes with for key}

Also remove your observer in deinit


For anyone who will be looking for the answer in the future, didChangeNotification will be posted only if changes are made on the same process, if you would like to receive all updates regardless of the process use KVO.

Apple doc

This notification isn't posted when changes are made outside the current process, or when ubiquitous defaults change. You can use key-value observing to register observers for specific keys of interest in order to be notified of all updates, regardless of whether changes are made within or outside the current process.

Here is a link to demo Xcode project which shows how to setup block based KVO on UserDefaults.