Published by Simon Schoeters

Bind user defaults to different identifier

I'm building a preference pane in Cocoa and would like to save the user defaults for this bundle (eg. save the status of a checkbox to “start automatically”). This works just like you would expect but... it writes your settings to the instead of the com.example.MyBundle defaults. This is not only ugly it's also dangerous when multiple bundles would overwrite each others preferences because they use the same variables. It's much cleaner to write the user defaults for your bundle to the com.example.MyBundle defaults, don't you think?

Some Cocoa code

It took me a while to find out how to do this. First you have to let go the idea to bind the user defaults for your preference pane in Interface Builder, that won't work. Remove all binding with the Shared User Defaults you created in Interface Builder.

Add a getter and setter method to one of your classes for each value you want to save. Below is an example for a boolean value. It looks a bit odd as you'll need CoreFoundation to read and write the values (please add a comment if you know a cleaner solution in Cocoa).

- (BOOL)getMyValue
  BOOL value = YES; // default value if not found
  CFStringRef key = CFSTR("MyValue");
  CFStringRef bundleID = CFSTR("com.example.MyBundle");
  CFPropertyListRef ref = CFPreferencesCopyAppValue(key, bundleID);

  if(ref && CFGetTypeID(ref) == CFBooleanGetTypeID())
    value = CFBooleanGetValue(ref);


  return value;

- (IBAction)setMyValue:(id)sender
  CFStringRef key = CFSTR("MyValue");
  BOOL value = (BOOL)[sender state];
  CFStringRef bundleID = CFSTR("com.example.MyBundle");
  CFPreferencesSetAppValue(key, value ? kCFBooleanTrue : kCFBooleanFalse, bundleID);

Interface Builder

Now that you have to code to read and write the boolean value you can fix your interface. Drag a connection from the checkbox to your class and select the ‘setMyValue’ method. If you run this bundle and click the checkbox it will write the status to the bundle with the bundle identifier you specified.

Check with defaults

Build your bundle and add it to the System Preferences (double click the build file). Select or deselect the checkbox and close the System Preferences. Now, open Terminal and run the following commands:

defaults read | grep MyValue
defaults read com.example.MyBundle

The first command should not return your value, the second one should.