Suffix

Bind user defaults to different identifier

Save perferences for a Mac OS X bundle.

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

Some Cocoa code

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

Add getter and setter methods to the class 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.

- (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);
  if(ref)
    CFRelease(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);
  CFPreferencesAppSynchronize(bundleID);
}

Interface Builder

Now that you have code to read and write the boolean value you can fix the 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 the 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 com.apple.systempreferences | grep MyValue
defaults read com.example.MyBundle

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