Friday, January 04, 2008

Cocoa: Preferences

Sometimes you want to save some user preferences for your app. Luckily there is good support for this in Mac OS X.
Perferences are key-value pairs that can be stored in the filesystem. The system works in multiple layers which means that if a key is not found in an upper layer, it will be searched in the layer below. Layers are: command line, application, global, language and default. This is means also that you don't need any additional code to see if a property is given on the command line or in the preferences etc - that is all transparent.
A good idea is to always provide the fallback values, so your code never needs to check if a preferences key is actually present or not.

Before your preferences show up in the Filesystem as user database under your choosen id, you have to fill the program id in Info.plist at:

<key>CFBundleIdentifier</key>
<string>XXX</string>

With the XXX the prefernces store would be ~/Library/Preferences/XXX.plist

An easy approach to set up the defaults if you only have a few preferences items is:

NSUserDefaults *preferences = [[NSUserDefaults standardUserDefaults] retain];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
  [NSNumber numberWithInt:11] ,@"interval",
  @"127.0.0.2" , @"targetAddr",
  NO, @"logEvents",
  @"osa:@shimoUp.aplescript", @"upScript",
  @"osa:@shimoDown.aplescript", @"downScript",
  nil ]; // terminate the list
[preferences registerDefaults:dict];


If you have more than a few items, it is easier to provide a defaults file in the app bundle and load this instead:

NSUserDefaults *preferences = [[NSUserDefaults standardUserDefaults] retain];
NSString *file = [[NSBundle mainBundle]
  pathForResource:@"Defaults" ofType:@"plist"];

NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:file];
[preferences registerDefaults:dict];


After this is done, reading preference values is easy:


NSString *someString = [preferences objectForKey:@"targetAddr"];
int interv = [preferences integerForKey:@"interval"];

You might want to use the respective xxxForKey message depending on the type of preferences values.

Storing modified values is equally easy:

[preferences setInteger:someInt forKey:@"interval"];
[preferences setObject:someString forKey:@"targetAddr"];


The documentation for NSUserDefaults states that it will store the modifications from time to time to the underlying filesystem object, but I found it better to explicitly call [preferences synchronize] to trigger this.

No comments: