The functions CFPreferencesSetAppValue
and CFPreferencesCopyAppValue
are the most straightforward way for an application to create and retrieve a preference that is specific to the current user and application. The preference data is written to the default domain (Current User, Current Application, Any Host) and so it will be available on all machines that this user can log into. These functions should never be called with kCFPreferencesAnyApplication
, only a true application ID or kCFPreferencesCurrentApplication
.
Preferences are stored as key/value pairs. The key must be a CFString object, but the value can be any Core Foundation property list value (see Property List Programming Topics for Core Foundation), including the container types. For example, you might have a key called defaultWindowWidth
which defines the width in pixels of any new windows that your application creates. Its value would most likely be of type CFNumber. You might also decide to combine window width and height into a single preference called defaultWindowSize
and make its value be a CFArray object containing two CFNumber objects.
The code in Listing 1 demonstrates how to create a simple preference for the application “MyTextEditor”. The example sets the default text color for the application to blue.
Listing 1 Writing a simple default
CFStringRef textColorKey = CFSTR("defaultTextColor"); |
CFStringRef colorBLUE = CFSTR("BLUE"); |
// Set up the preference. |
CFPreferencesSetAppValue(textColorKey, colorBLUE, |
kCFPreferencesCurrentApplication); |
// Write out the preference data. |
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); |
Notice that CFPreferencesSetAppValue
by itself is not sufficient to create the new preference. A call to CFPreferencesAppSynchronize
is required to actually save the value. If you are writing multiple preferences, it is more efficient to sync only once after the last value has been set than to sync after each individual value is set. For example, if you implement a preference panel you might only synchronize when the user presses an “OK” button. In other cases you might not want to sync at all until the application quits—although note that, of course, if the application crashes all unsaved preferences settings will be lost.
The simplest way to locate and retrieve a preference value is to use the CFPreferencesCopyAppValue
function. This call searches through the various preference domains in order until it finds the key you have specified. If a preference has been set in a less-specific domain—”Any Application”, for example —its value will be retrieved with this call if a more specific version cannot be found. Listing 2 shows how to retrieve the text color preference saved in Listing 1.
Listing 2 Reading a simple default
CFStringRef textColorKey = CFSTR("defaultTextColor"); |
CFStringRef textColor; |
// Read the preference. |
textColor = (CFStringRef)CFPreferencesCopyAppValue(textColorKey, |
kCFPreferencesCurrentApplication); |
// When finished with value, you must release it |
// CFRelease(textColor); |
Note that all values returned from preferences are immutable, even if you have just set the value using a mutable object.
An example of simple preference updating is a game that searches for a high score preference each time a round is completed. If there is no high score preference, the application writes the current score as the high score. If a high score preference exists, it is compared with the new score and updated if the new score is higher. Listing 3 demonstrates this process.
Listing 3 Updating a preference
CFStringRef highScoreKey = CFSTR("High Score"); |
CFNumberRef tempScore; |
int highScore; |
// Look for the preference. |
tempScore = (CFNumberRef)CFPreferencesCopyAppValue(highScoreKey, |
kCFPreferencesCurrentApplication); |
// If the preference exists, update it. If not, create it. |
if (tempScore) |
{ |
// Numbers come out of preferences as CFNumber objects. |
if (!CFNumberGetValue(tempScore, kCFNumberIntType, &highScore)) { |
highScore = 0; |
} |
CFRelease(tempScore); |
printf("The old high score was %d.", highScore); |
} |
else |
{ |
// No previous value. |
printf("There is no old high score."); |
highScore = 0; |
} |
highScore += 5; |
// Create the CFNumber to pass to the preference API. |
tempScore = CFNumberCreate(NULL, kCFNumberIntType, &highScore); |
// Set the preference value, or update it if it already exists. |
CFPreferencesSetAppValue(highScoreKey, tempScore, |
kCFPreferencesCurrentApplication); |
// Release the CFNumber. |
CFRelease(tempScore); |
// Write out the preferences. |
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); |
The technique shown in Listing 3 generalizes to context of multiple preferences where an application tries to locate a set of preferences for display to the user in a graphical preference panel. If no preferences exist, default values are used. If existing preference values are found, they are used to initialize the preference panel for display to the user. After the user makes changes and pushes the “OK” button, you can set the changed preference values and write them to storage.
Last updated: 2006-10-03