Preferences Best Practices

CFPreferences is Apple’s standard API for storing and retrieving preference keys and values, allowing the calling process to concentrate on native types and key-value pair meanings rather than the mechanisms of writing to files and so on. While it provides a convenient API, it is also easy to use incorrectly. This document gives an overview of when it is appropriate to use what API, and how to synchronize preferences across process boundaries.

When To Use What API

The following general guiding principles apply to the CFPreferences API:

Note that, although they are treated separately in the documentation, high-level API and low-level API are not exclusive. It may be appropriate to use high-level API in some parts of an application, and low-level API in another. For example, you can set a preference key/value pair with CFPreferencesSetValue(key, value, app, user, host) and then read it with CFPreferencesCopyAppValue(key, value, app)— indeed you probably do want to read it with the latter function since it traverses the search path.

High Level API

As much as possible, you should use CFPreferencesCopyAppValue to retrieve preference keys. This function traverses the search path looking for the matching key and returns the value from the most-specific domain.

Users of particular machines may also be subject to “management” through “Workgroup Manager” or the “Capabilities” option of the Accounts preference pane in System Preferences. Either of these mechanisms may force preference values on the user. These values are also picked up by the CFPreferencesCopyAppValue API—you should use this function to ensure your application properly responds to management of this kind.

Low Level API

If your application needs to distinguish between “the current host” and “any host” then you use the low level API. If for some reason you need to search for a key-value pair in a specific domain, you should use CFPreferencesCopyValue—you should not use this function as a general retrieval mechanism.

Synchronizing Preferences Across Process Boundaries

The Rule of Thumb on CFPreferences synchronization:

  1. Only synchronize when absolutely necessary

  2. If you have to communicate across process boundaries, use notifications with appropriate granularity, keeping 1) in mind. Typically, this means setting a flag in client processes, and only if a changed key is required should you trigger a synchronize.

Many processes in OS X write a preference key-value pair for use in another process. While it would be convenient for preference key-value pairs to auto-update in other processes, guaranteeing auto-update in all circumstances would incur a performance penalty and also make it difficult to ensure related preferences are read and written consistently. Processes should always have the choice of when to elect to accept new information into their space. For preference values, CFPreferencesSynchronize and CFPreferencesAppSynchronize are the function calls that providing the information choke-point. You should typically not, however, call these functions before every read of a preference key.

Preference File Locations and Debugging

Preferences files are stored in the system’s or user’s preferences directories. On OS X versions 10.0 to 10.4 these are in /Library/Preferences and in /Library/Preferences in the user’s home directory respectively. When debugging an application, it may sometimes be useful to inspect these files to determine that preferences have been saved correctly, however you should never hardcode these paths into an application. If you do need to access the directory programmatically you should use the NSSearchPathForDirectoriesInDomains API, although there should typically be no reason to do so.

Note that preferences you set up in the registration domain (see Defaults Domains in Preferences and Settings Programming Guide) are not stored in the preferences file. Put another way, the preferences file stores only values that are different from those in the registration domain, so you should not expect to see “default defaults” in the preferences file after you run your application.

Managed Preferences

OS X v10.2 introduced the concept of “managed preferences.” The function CFPreferencesAppValueIsForced determines whether or not a given key has been imposed on the user. For managed keys, you should disable any user interface that allows the user to modify the value for the key.