How to bind objects in NSDictionary in NSUserDefaults?

You need to add a NSObjectController in Interface Builder. In the attributes inspector tab leave the standard mode "Class" and class name on "NSMutableDictionary", but switch "Prepares Content" on. In the bindings inspector bind "Content Object" to "Shared User Defaults Controller", controller key "values" and "Model Key Path" to your dictionary's key in User Defaults. Switch "Handles Content As Compound Value" on.

Now you can bind your text field (or check boxes etc). values to the ObjectController, with controller key "selection" (!) and the keys in your dictionary (as model key path).

Hope this helps. I couldn't find the answer in Apple's documentation and also not in the net, was lucky to find it out myself by trying different controller objects...


First: @keeluu's answer should be marked correct.

Doing it Programmatically

If you're doing this in code rather than through IB, there's a "gotcha" to look out for. It looks like this:

// Assume 'anObjectController' is an NSObjectController.
// Assume 'userDefaultsController' is [NSUserDefaultsController sharedUserDefaultsController]
// Assume 'someDictionary' is an NSDictionary in userDefaults. 

[self.anObjectController 
    bind:NSContentBinding 
    toObject:userDefaultsController 
    withKeyPath:@"values.someDictionary"
    options:@{NSHandlesContentAsCompoundValueBindingOption: @YES}];

If you do the above, you'll find that when you bind your UI elements to properties in someDictionary, those UI elements will correctly display the values that are IN user defaults, but when you change them (by say, clicking a bound checkbox) those values will NOT be updated in user defaults; the old values stick around.

The Reason

The binding must be to NSContentObjectBinding instead of NSContentBinding. The NSObjectController class exposes a binding named contentObject. When you bind to that, everything works properly.

This is an easy mistake to make because Xcode's code completion automatically goes for NSContentBinding and it's one of the most frequently used binding names anyway. This just cost me hours of trouble, so hopefully I saved someone else some time.