Question

I'm subclassing NSUserDefaults in my application. A side effect of this is I can't use [NSUserDefaults sharedUserDefaults], I have to have a class method to provide my own static defaults object. This isn't been a problem in code, but it's proving tricky now that I'm hooking up the preferences UI with bindings.

The shared NSUserDefaultsController uses the shared defaults, so that's out. Instead I can create my own defaults controller in my window controller, provide it with my static defaults object, and hook up my bindings to that. This doesn't fully work though. When I tried using KVO on my defaults object I didn't receive any change notifications. I tried this again with a regular NSUserDefaults object (not a subclass) and again, no KVO notifications. Substituting in the shared defaults object, KVO works exactly how I'd expect.

Does anyone have any ideas on how I can get bindings and KVO to work when I'm not using the shared defaults?

Was it helpful?

Solution 4

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.

OTHER TIPS

I always wrap the NSUserDefaults stuff with my own custom class to make sure everything works as expected.

The workflow essentially goes like this:

  1. When the application launches, read all of the required NSUserDefaults entries into the custom class. This custom class has a property for each of the settings, and all of the NSDictionary and NSArray objects are mutable so that changes can be made.
  2. Any user interface elements that make changes to the settings are bound to the properties of the custom class
  3. Changes to the settings are saved to NSUserDefaults as required.

I find that this encapsulation of the application settings helps to take away all of the headaches. Instead of dealing directly with Apple's magical NSUserDefaults system, you spend your time interacting with a well-defined and easily tested custom class.

The documentation provides the answer, I think. The init method initializes a new instance, but doesn't put anything in the search path. If you're initializing your own instances, you'll need to set up the domains you intend to use by hand. My guess would be that your application isn't working because the values that you're trying to get/set don't exist because there are no domains to save them in.

In particular, look at the addSuiteNamed: method and the NSUserDefaultsDomain constants. You'll need to set these up by hand to make your subclass, or any instance of NSUserDefaults other than standardUserDefaults, work properly.

I've been doing something similar just recently, and I'm having pretty good luck so far. I create a standard NSObject subclass (MYUserDefaults for argument). It has properties for the things I want (host, username, etc.) Some go to NSUserDefaults, some go to keychain or the like. It has a standard +sharedUserDefaults singleton method. (*)

In the window owner (NSWindowController or AppDelegate), I provide a -sharedUserDefaults. I then bind through that to the settings I want with a simple keypath (sharedUserDefaults.host). I typically avoid keypaths in bindings because they often obfuscate things, but here I think it keeps everything clear while taking care of the special case that the NIB needs to be prevented from creating an extra non-singleton version.

(*) No fancy "forced singleton" stuff (which might also fix your problem). Am I the only one who thinks Apple should have big flashing "don't use this unless you know why you need it, and then don't use it anyway" sign on this doc? I find that code dangerous to let inexperienced developers see. Half the objects in the last project I worked on had +allocWithZone: overloaded. To me it's just bad form to call +alloc, and get pointers to existing objects. Don't lie to me unless it's absolutely necessary.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top