53

Let's say I have a key @"MyPreference", with a corresponding value stored through NSUserDefaults.

Is there a way to be notified when the value is modified?

Or could it be done through bindings? (But this case, instead of binding the value to a UI element, I wish my object to be notified of the change, so that I can perform other tasks.)

I am aware that NSUserDefaultsDidChangeNotification can be observed, but this appears to be a all-or-nothing approach, and there does not appear to be a mechanism there to get at the specific key-value-pair that was modified. (Feel free to correct.)

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
SirRatty
  • 1,641
  • 3
  • 17
  • 18

4 Answers4

81

Spent all day looking for the answer, only to find it 10 minutes after asking the question...

Came across a solution through Key-Value-Observing:

[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self
    forKeyPath:@"values.MyPreference"
    options:NSKeyValueObservingOptionNew
    context:NULL];

Or, more simply (per comment below):

[[NSUserDefaults standardUserDefaults] addObserver:self
                                        forKeyPath:@"MyPreference"
                                           options:NSKeyValueObservingOptionNew
                                           context:NULL];
paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
SirRatty
  • 1,641
  • 3
  • 17
  • 18
  • 14
    You could also call this method on `[NSUserDefaults standardUserDefaults]` and not have to prepend the "values." to the key path. The other parameters remain unchanged. – Quinn Taylor Feb 26 '11 at 06:51
  • 6
    I wasn't able to get it to work as Quin suggested using `[NSUserDefaults standardUserDefaults]`. However, the string append is not needed as `...forKeyPath:@"values.MyPreference"...` will work. Also, you will need to implement `- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;` to actually capture the event. – TrevorL Jun 16 '11 at 19:30
  • Hi TrevorL...even though stringByAppendingString: is not necessary, it helps when creating a generic method - (void) observePreference:(NSString *)pref { ...[@"values." stringByAppendingString:pref] ... – Arvin Jan 15 '12 at 02:05
  • 1
    but this approach is only for Mac, not iOS – M.Y. May 22 '12 at 11:51
  • The `NSKeyValueObservingOptionNew` option isn't necessary. You can pass `0` as options. – Flovdis Mar 03 '14 at 21:04
  • In iOS seems to work only with the `standardUserDefaults` and not with custom `suiteName`'s defaults. – Rivera Apr 01 '15 at 17:29
  • 1
    The first solution (that uses `NSUserDefaultsController`) works for Mac, but the second solution doesn't work. – Ethan Oct 18 '15 at 00:58
  • Does KVO work when the user defaults is part of a shared suite and the values were changed by another process? – adib Apr 23 '17 at 22:33
23

Swift:

override func viewDidLoad() {
  super.viewDidLoad()
  NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: "THE KEY", options: NSKeyValueObservingOptions.New, context: nil)
}

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
  // your logic
}

deinit {
  NSUserDefaults.standardUserDefaults().removeObserver(self, forKeyPath: "THE KEY")
}
Shahzad Barkati
  • 2,532
  • 6
  • 25
  • 33
Brian
  • 30,156
  • 15
  • 86
  • 87
11

And Apple employee advised to use NSUserDefaultsDidChangeNotification notification over here: https://devforums.apple.com/message/237718#237718

DenNukem
  • 8,014
  • 3
  • 40
  • 45
  • 8
    The disadvantage of this is, you cannot know WHICH of the settings was changed. – Emmanuel Jan 05 '12 at 21:23
  • 2
    Yes, you'd think they would have put at least the changed key(s) in the userInfo dictionary of NSUserDefaultsDidChangeNotification, but no: "This notification does not contain a userInfo dictionary." Even worse, the only specification of when NSUserDefaultsDidChangeNotification is posted says that it is posted "when a persistent domain is changed". It would probably take me 20 minutes of research to confirm exactly what that means, and then I'm not sure I'd trust it. – Jerry Krinock Oct 24 '13 at 16:51
  • This method has another disadvantage: If an external program changes your app's defaults, e.g. using the cmd "defaults write" or with the Prefs Editor app, then you won't get notified. Using the KVO technique will notify you, OTOH. – Thomas Tempelmann May 06 '19 at 21:35
0

I agree with @DenNukem. I was using the NSKeyValueObservingOptionNew. But this function started giving me the BAD_ACCESS Code=1 error wherever I used the NSUserDefault in order to save other objects. In case you are using Key-Value Observer (KVC), just be aware of the Zombie issue on NSUserDefaults.

Here is the link to the solution: NSUserDefaults and KVO issues

ricardopereira
  • 11,118
  • 5
  • 63
  • 81