10

I'm using NSUserDefaults in my app and I would like to be notified when a particular value is changed. For that, I added the following lines in viewDidLoad:

NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
[settings synchronize];
[settings addObserver:self forKeyPath:@"pref_server" options:NSKeyValueObservingOptionNew context:NULL];

And the method to be notified:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{

    NSLog(@"Change");

    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    if (object == settings && [keyPath isEqualToString:@"pref_server"])
    {
        NSLog(@"Server did change");
    }

}

Unfortunately, the latter is never called...@"pref_server" is the item identifier I have set in Root.plist, in Settings.bundle. What am I doing wrong?

J0o0
  • 179
  • 1
  • 6
  • On a somewhat unrelated note, the documentation for `synchronise()` states: "Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used." – Yunus Nedim Mehel May 14 '19 at 09:40
  • If in trouble, please observe this very detailed blog article from one of the authors of NSUserdefaults: http://dscoder.com/defaults.html – Thomas Tempelmann Aug 23 '21 at 15:36

5 Answers5

19

I suggest making use of the appropriate notification: NSUserDefaultsDidChangeNotification.

Search for AppPrefs in the Apple Documentation within Xcode and it'll show an example app which does exactly what you want to do. Just compile and run! It makes use of the NSUserDefaultsDidChangeNotification.

This is the code being used to register an observer:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(defaultsChanged:)
                                             name:NSUserDefaultsDidChangeNotification
                                           object:nil];
pkamb
  • 33,281
  • 23
  • 160
  • 191
Nick Weaver
  • 47,228
  • 12
  • 98
  • 108
  • For Swift3: `NotificationCenter.default.addObserver(self, selector: #selector(defaultsChanged), name: UserDefaults.didChangeNotification, object: nil)` – Yohst Jun 08 '17 at 15:55
  • 1
    The notification is not sent if the change is happening in another process, so KVO is the only way to check for changes on values on keys of the NSUserDefaults shared between the main Application and its Extensions (Today Widget, etc.) – valeCocoa Sep 03 '18 at 14:56
8

Interesting observation:

[NSUserDefaults standardUserDefaults] seems to be KVO compliant now as I am able to observe and bind to it's values. I'm running 10.7.2, using Xcode 4.2, SDK 10.7, LLVM compiler 3.0 .

I can't seem to find this new behavior documented anywhere in the release notes.

Nagendra Rao
  • 7,016
  • 5
  • 54
  • 92
matt spark
  • 89
  • 1
  • 1
  • 1
    I would assume it is an undocumented change in Lion. The release notes mention some changes under the hood made to defaults, so I guess KVO support changed at the same time. The need to use the controller for KVC/KVO is documented in the Cocoa Bindings Programming Topics for both Snow Leopard and Lion. – Mark Lilback Dec 06 '11 at 23:08
  • 3
    The question was specifically about [tag:ios]. – Ben Collins Mar 04 '14 at 17:17
  • Documentation states that it is compliant: "...You can use key-value observing to be notified of any updates to a particular default value...": developer.apple.com/documentation/foundation/userdefaults – Yunus Nedim Mehel May 15 '19 at 09:53
3

Though its not well documented, NSUserDefaults do support key-value observing in iOS7.

Kelvin
  • 1,082
  • 2
  • 12
  • 23
3

NSUserDefaults is not KVO compliant, but NSUserDefaultsController is. So you'd use:

NSUserDefaultsController *defaultsc = [NSUserDefaultsController sharedUserDefaultsController];
[defaultsc addObserver:self forKeyPath:@"values.pref_server" 
               options:NSKeyValueObservingOptionNew 
               context:NULL];
Mark Lilback
  • 1,154
  • 9
  • 21
  • 12
    NSUserDefaultsController ist not available in Cocoa Touch for iOS. – Yang Meyer Dec 07 '11 at 13:39
  • 1
    Documentation states that it is compliant: "...You can use key-value observing to be notified of any updates to a particular default value..." https://developer.apple.com/documentation/foundation/userdefaults – Yunus Nedim Mehel May 14 '19 at 09:38
2

As of iOS 11.3 this works and is documented:

Responding to Defaults Changes

You can use key-value observing to be notified of any updates to a particular default value.

Community
  • 1
  • 1
Mojo66
  • 1,109
  • 12
  • 21