Nsuserdefaults and Kvo Issues

NSUserDefaults and KVO issues

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];

KVO and Bindings problems using my own (not the shared) NSUserDefaults object

Since I haven't found a solution yet, I decided to take some advice given to me during the last CocoaHeads Syracuse meeting and switch to using categories instead of subclassing NSUserDefaults. Not a perfect solution, but it'll let me get past this problem and back to work.

KVO with shared NSUserDefaults in Swift

As of iOS 10 you can use KVO on User Defaults.

This has already been answered around here so I will not re-address it.

Old Answer (iOS 9 and older)

The short answer is that you can't use KVO or even NSNotificationCenter on NSUserDefaults to communicate changes between an App Extension and the containing App.

There's a great post by Atomic Bird that looks at the ways of coordinating communication. In particular its interesting to look at his analysis of communicating user defaults changes:

A possible alternative for app/extension notifications is to use the
Darwin notification center via
CFNotificationCenterGetDarwinNotifyCenter, which actually is a little
bit like NSDistributedNotificationCenter. There's some discussion of
this by Wade Spires at Apple's dev forums site.

I say "possible" because I'm not 100% confident of this continuing to
work. In the documentation for this method, Apple notes that

An application has only one Darwin notification center, so this
function returns the same value each time it is called.

So although this is apparently legal, it also sounds a lot like it
violates the philosophy inherent in app extension restrictions, viz,
that they can't access anything from the hosting app. This is why
[UIApplication sharedApplication] is off limits in extensions. I can't
help wonder if allowing CFNotificationCenterGetDarwinNotifyCenter is
an oversight that might get "fixed" at some point.

So I guess for now a good solution might be to use MMWormhole as they implement the above solution.

Your other option is to use to check the user defaults every time your App becomes active and confirm whether any keys have changed, posting the relevant notifications etc.

Good luck

iOS exc_bad_access when observing NSUserDefaults with KVO and delayedSelector

NSUserDefaults is not documented to be KVO compliant so it's not possible to observe defaults by their key. This might be the reason for the crash but without a stack trace it's not possible to tell.

There is a notification you can register for that announces changes to the defaults system: NSUserDefaultsDidChangeNotification.

How to use KVO for UserDefaults in Swift?

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()
}

Cocoa - Notification on NSUserDefaults value change?

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];

NSUserDefaultsDidChangeNotification: What's the name Of the Key, that Changed?

As others stated, there is no way to get the info about the changed key from the NSUserDefaultsDidChange Notification. But there is no need to duplicate any content and check for yourself, because there is Key Value Observing (KVO) which also works with the NSUserDefaults, if you need to specifically be notified of a certain property:

First, register for KVO instead of using the NotificationCenter:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults addObserver:self
forKeyPath:@"nameOfThingIAmInterestedIn"
options:NSKeyValueObservingOptionNew
context:NULL];

don't forget to remove the observation (e.g. in viewDidUnload or dealloc)

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObserver:self forKeyPath:@"nameOfThingIAmInterestedIn"];

and finally implement this method to receive KVO notifications

-(void)observeValueForKeyPath:(NSString *)keyPath 
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(@"KVO: %@ changed property %@ to value %@", object, keyPath, change);
}


Related Topics



Leave a reply



Submit