27

I'm developing an iOS application with latest SDK.

I want to know when a property on NSUserDefaults changes it value.

I have found this, but it is specific for macOS:

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

How can I do this on iOS?

pkamb
  • 33,281
  • 23
  • 160
  • 191
VansFannel
  • 45,055
  • 107
  • 359
  • 626
  • KVO is the same on iOS and Mac - so if the code works on Mac, it should also work on iOS. However, I do not think you should observe the "values.myPreference", but rather just "myPreference", on the correct object. – Motti Shneor Jan 31 '19 at 13:38

5 Answers5

39

try out the NSUserDefaultsDidChangeNotification with this code snippet:

- (id)init {

  self = [super init];

  if(self) {
     [[NSNotificationCenter defaultCenter] addObserver:self
                                              selector:@selector(defaultsChanged:)
                                                  name:NSUserDefaultsDidChangeNotification
                                                object:nil];
  }
  return self;    
}

- (void)defaultsChanged:(NSNotification *)notification {
  // Get the user defaults
  NSUserDefaults *defaults = (NSUserDefaults *)[notification object];

  NSLog(@"%@", [defaults objectForKey:@"yourIntrestedObject"]);
}

- (void)dealloc {
 [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Kévin Renella
  • 1,007
  • 9
  • 20
CarlJ
  • 9,461
  • 3
  • 33
  • 47
  • Thank you very much for your answer. If I want to get notified only when specifics keys change, how can I do it? – VansFannel Mar 04 '13 at 16:55
  • maybe you can give @Dmitry second solution a try. – CarlJ Mar 04 '13 at 17:02
  • this code has a problem, the notifictionCenter won't actually have a NSUSerDefaults object because the object is nil. The only way to get the notification object to be posted within the notificationCenter is to fill out the object: parameter. – TheM00s3 Jun 04 '15 at 17:31
  • 5
    @TheM00s3 You're incorrect. Populating the `object` param of `addObserver:selector:name:object` only causes the selector to be called if the object posting the notification is the same object passed into the param. If `nil` is passed, the selector will be called for any object posting the notification. An `NSUserDefaults` object will be the `object` of this `NSNotification` in this case (as long as someone didn't randomly post a `NSUserDefaultsDidChangeNotification` themselves). – gyratory circus Jun 08 '16 at 20:10
  • 1
    I don't like this - because you unnecessarily register for ANY change in the user defaults. Not just your property of interest. So you will receive lots of undesired calls, and will also need to filter them (digging into the notification, looking for your property) every time. Less than optimal – Motti Shneor Jan 31 '19 at 13:40
  • One more point for Carl my old collegue ;-) – blackjacx May 11 '21 at 13:07
22

Use NSUserDefaultsDidChangeNotification for notification about change in User defaults:

[[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(defaultsDidChange:) name:NSUserDefaultsDidChangeNotification
    object:nil];

// notification
- (void)defaultsDidChange:(NSNotification *)aNotification
{
     //
}

Use KVO for notification about specific change in User defaults:

[[NSUserDefaults standardUserDefaults] addObserver:self 
    forKeyPath:@"APXMyPropertyIamInterestedInKey" options:NSKeyValueObservingOptionNew
    context:NULL];

// KVO handler
-(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject
    change:(NSDictionary *)aChange context:(void *)aContext 
{
    // 
}
Dmitry Vodianyk
  • 395
  • 3
  • 4
  • 7
    I find that in iOS 7 KVO works well within the app, but does not work if the keyPath is changed by the Settings. – SwiftArchitect May 11 '14 at 16:31
  • Tried `KVO` approach, and use `removeObserver:forKeyPath:` method in `dealloc`. When the execution point reaches to `removeObserver:forKeyPath`, the app throws an exception, saying **Cannot remove an observer ... because it is not registered as an observer.** – steveluoxin Oct 17 '17 at 02:07
3

If you need to use this in a Swift 4 project you can use the following:

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(defaultsChanged), name: UserDefaults.didChangeNotification, object: nil)
}

@objc func defaultsChanged() {
    // Your code
}
Josh
  • 254
  • 3
  • 19
2

In Xcode help, the overview of UserDefaults class (the swifty name for NSUserDefaults) clearly states:

You can use key-value observing to register observers for specific keys of interest in order to be notified of all updates to a local defaults database. For more details, see Key-Value Observing Programming Guide."

I'll see if I can come up with a quick code-sample, but stack-overflow must be full of KVO samples in swift.

Motti Shneor
  • 2,095
  • 1
  • 18
  • 24
1

Here is solution i posted on how to do this in Swift and also get the userInfo:

How to determine when Settings change on iOS

pkamb
  • 33,281
  • 23
  • 160
  • 191
Joseph
  • 5,793
  • 4
  • 34
  • 41