Reputation: 3471
Is it possible to use KVO in a way that it detects not only if the value changed, but also if the same value was set again? I'm currently receiving notifications only when the value changed (is different from the previously set one). I need to receive notification every time the value is set (even if it's the same as the one previously set). How can I achieve this?
My code:
private func addObserver() {
defaults.addObserver(self, forKeyPath: DefaultsKeys.testKey._key, options: .new, context: nil)
}
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let value = change?[NSKeyValueChangeKey.newKey] as? Bool else { return }
statusCallback?(value)
}
private func removeObserver() {
defaults.removeObserver(self, forKeyPath: DefaultsKeys.testKey._key)
}
Upvotes: 1
Views: 1368
Reputation: 998
If you want to track every setting a value in NSUserDefaults
, even if the new value is the same as previous, wrap the value with the NSDictionary
, and put inside the dictionary an NSUUID
value, generated every time as a new setValue
being called.
Before (observeValueForKeyPath
did not called on every setValue
):
[self.mySharedDefaults setValue: @"CHECKING" forKey:@"appStatusOUT"];
After (observeValueForKeyPath
being called on every setValue
):
[self.mySharedDefaults setValue: [NSDictionary dictionaryWithObjectsAndKeys: @"CHECKING", @"CMD",
[NSUUID UUID].UUIDString, @"UUID", nil] forKey:@"appStatusOUT"];
Upvotes: -1
Reputation: 6058
KVO mechanism is very simple - it does not perform any additional checks upon setting a new value, it is merely triggered when a setter is called. Hence it is not possible to differentiate if the value is different from that which is already being set. And it's good. Firstly, because, it's not typical in practice to assign the same value to a variable.
Second, introducing additional checks would be consumable and in most cases unneeded. If that check existed, it would negatively affect performance.
That being said, as far as Swift is concerned, you can consider replacing KVO-mechanism (essentially an Objective-C legacy) with native Swift property observers: willSet
and didSet
. And this would essentially play the same role as by passing both options: NSKeyValueObservingOptionNew
and NSKeyValueObservingOptionOld
(.old
and .new
in Swift) to addObserver
method. Once having specified these flags, whenever KVO mechanism is triggered, you will receive both values (old an new) in observeValue(...)
, from where you can decide what to do with either. But why would you need such complexity when willSet
does practically the same and is much more conveinent:
var myVariable : String! {
willSet {
print("Old value is: \(myVariable)")
print("New value is: \(newValue)")
// Let's do something with our old value
}
didSet {
print("Just set the new value: \(newValue)")
// New value is set. Let's do some actions.
}
}
Upvotes: 0
Reputation: 437412
KVO generally is called every time the observed property is set, even if it's the same value it was last time. But I guess you're observing UserDefaults
, and which has an idiosyncrasy that prevents this from happening (probably an optimization that prevents unnecessary saves of the store).
You can register for .didChangeNotification
, which appears to called whether the value changed or not:
NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: .main) { notification in
print("notification", notification)
}
Upvotes: 2
Reputation: 4555
You can achieve this by doing like:
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
let value = change?[.oldKey] as? Bool
guard value == nil || value != yourVariableToCheck else { return }
statusCallback?(value)
}
Only change yourVariableToCheck on your own variable.
Upvotes: 0