How to use KVO for UserDefaults in Swift?

foundation, ios, key-value-observing, swift

Solution

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()
}

Problem

I'm rewriting parts of an app, and found this code: ``` fileprivate let defaults = UserDefaults.standard func storeValue(_ value: AnyObject, forKey key:String) { defaults.set(value, forKey: key) defaults.synchronize() NotificationCenter.default.post(name: Notification.Name(rawValue: "persistanceServiceValueChangedNotification"), object: key) } func getValueForKey(_ key:String, defaultValue:AnyObject? = nil) -> AnyObject? { return defaults.object(forKey: key) as AnyObject? ?? defaultValue } ``` When CMD-clicking the line `defaults.synchronize()` I see that `synchronize` is planned deprecated. This is written in the code: ``` /*! -synchronize is deprecated and will be marked with the NS_DEPRECATED macro in a future release. -synchronize blocks the calling thread until all in-progress set operations have completed. This is no longer necessary. Replacements for previous uses of -synchronize depend on what the intent of calling synchronize was. If you synchronized... - ...before reading in order to fetch updated values: remove the synchronize call - ...after writing in order to notify another program to read: the other program can use KVO to observe the default without needing to notify - ...before exiting in a non-app (command line tool, agent, or daemon) process: call CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication) - ...for any other reason: remove the synchronize call */ ``` As far as I can interpret, the usage in my case fits the second description: synchronizing after writing, in order to notify others. It suggests using KVO to ovserve, but how? When I search for this, I find a bunch of slightly older Objective-C-examples. What is the best practice for observing UserDefaults?

Original source

Related problems