How to use KVO for UserDefaults in Swift?
From the blog of David Smith http://dscoder.com/defaults.html https://twitter.com/catfish_man/status/674727133017587712
If one process sets a shared default, then notifies another process to read it, then you may be in one of the very few remaining situations that it's useful to call the -synchronize method in: -synchronize acts as a "barrier", in that it provides a guarantee that once it has returned, any other process that reads that default will see the new value rather than the old value.
For applications running on iOS 9.3 and later / macOS Sierra and later, -synchronize is not needed (or recommended) even in this situation, since Key-Value Observation of defaults works between processes now, so the reading process can just watch directly for the value to change. As a result of that, applications running on those operating systems should generally never call 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
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()
}