About the User Defaults System

The user defaults system manages the storage of preferences for each user. Most preferences are stored persistently and therefore do not change between subsequent launch cycles of your app. Apps use preferences to track user-initiated and program-initiated configuration changes.

What Makes a Good Preference?

When defining your app’s preferences, it is better to use simple values and data types whenever possible. The preferences system is built around property-list data types such as strings, numbers, and dates. Although you can use an NSData object to store arbitrary objects in preferences, doing so is not recommended in most cases.

Storing objects persistently means that your app has to decode that object at some point. In the case of preferences, a stored object means decoding the object every time you access the preference. It also means that a newer version of your app has to ensure that it is able to decode objects created and written to disk using an earlier version of your app, which is potentially error prone.

A better approach for preferences is to store simple strings and values and use them to create the objects your app needs. Storing simple values means that your app can always access the value. The only thing that changes from release to release is the interpretation of the simple value and the objects your app creates in response.

Providing a Preference Interface

For user-facing preferences, Table 1-1 lists the options for displaying those preferences to the user. As you can see from this table, most options involve the creation of a custom user interface for managing and presenting preferences. If you are creating an iOS app, you can use a Settings bundle to present preferences, but you should do so only for settings the user changes infrequently.

Table 1-1  Options for displaying preferences to the user

Preference

iOS

OS X

Frequently changed preferences

Custom UI

Custom UI

Infrequently changed preferences

Settings bundle

Custom UI

Preferences in Mac apps should be accessible from a Preferences menu item in the app menu. Cocoa apps created using the Xcode templates provide such a menu item for you automatically. It is your responsibility to present an appropriate user interface when the user chooses this menu item. You can provide that user interface by defining an action method in your app delegate that displays a custom preferences window and connecting that action method to the menu item in Interface Builder.

There is no standard way to display custom preferences from inside an iOS app. You can integrate preferences in many ways, including using a separate tab in a tab-bar interface or using a custom button from one of your app’s screens. Preferences should generally be presented using a distinct view controller so that changes in preferences can be recorded when that view controller is dismissed by the user.

The Organization of Preferences

Preferences are grouped into domains, each of which has a name and a specific usage. For example, there’s a domain for app-specific preferences and another for systemwide preferences that apply to all apps. All preferences are stored and accessed on a per-user basis. There is no support for sharing preferences between users.

Each preference has three components:

The lifetime of a preference depends on which domain you store it in. Some domains store preferences persistently by writing them to the user’s defaults database. Such preferences continue to exist from one app launch to the next. Other domains store preferences in a more volatile way, preserving preference values only for the life of the corresponding user defaults object.

A search for the value of a given preference proceeds through the domains in an NSUserDefaults object’s search list. Only domains in the search list are searched and they are searched in the order shown in Table 1-2, starting with the NSArgumentDomain domain. A search ends when a preference with the specified name is found. If multiple domains contain the same preference, the value is taken from the domain nearest the beginning of the search list.

Table 1-2  Search order for domains

Domain

State

NSArgumentDomain

volatile

Application (Identified by the app’s identifier)

persistent

NSGlobalDomain

persistent

Languages (Identified by the language names)

volatile

NSRegistrationDomain

volatile

The Argument Domain

The argument domain comprises values set from command- line arguments (if you started the app from the command line) and is identified by the NSArgumentDomain constant. Values set from the command line are automatically placed into this domain by the system. To add a value to this domain, specify the preference name on the command line (preceded with a hyphen) and follow it with the corresponding value. For example, the following command launches Xcode and sets the value of its IndexOnOpen preference to NO:

localhost> Xcode.app/Contents/MacOS/Xcode -IndexOnOpen NO

Preferences set from the command line temporarily override the established values stored in the user’s defaults database. In the preceding example, setting the IndexOnOpen preference to NO prevents Xcode from indexing projects automatically, even if the preference is set to YES in the user defaults database.

The Application Domain

The application domain contains app-specific preferences that are stored in the user defaults database of the current user. When you use the shared NSUserDefaults object (or a NSUserDefaultsController object in OS X) to write preferences, those preferences are automatically placed in this domain.

Because this domain is app-specific, the contents of the domain are tied to your app’s bundle identifier. The contents of this domain are stored in a file that is managed by the system. Currently, this file is located in the $HOME/Library/Preferences/ directory, where $HOME is either the app’s home directory or the user’s home directory (depending on the platform and whether your app is in a sandbox). The name of the user defaults database file is <ApplicationBundleIdentifer>.plist, where <ApplicationBundleIdentifer> is your app’s bundle identifier. You should not modify this file directly but can inspect it during debugging to make sure preference values are being written by your app.

The Global Domain

The global domain contains preferences that are applicable to all apps and is identified by the NSGlobalDomain constant. This domain is typically used by system frameworks to store system-wide values and should not be used by your app to store app-specific values. If you want to change the value of a preference in the global domain, write that same preference to the application domain with the new value.

Examples of how the system frameworks use this domain:

  • Instances of the NSRuleView class store the user’s preferred measurement units in the AppleMeasurementUnits key. Using this storage location causes ruler views in all apps to use the same units.

  • The system uses the AppleLanguages key to store the user’s preferred languages as an array of strings. For example, a user could specify English as the preferred language, followed by Spanish, French, German, Italian, and Swedish.

The Languages Domains

For each language in the AppleLanguages preference, the system records language-specific preference values in a domain whose name is based on the language name. Each language-specific domain contains preferences for the corresponding locale. Many classes in the Foundation framework (such as the NSDate, NSDateFormatter, NSTimeZone, NSString, and NSScanner classes) use this locale information to modify their behavior. For example, when you request a string representation of an NSCalendarDate object, the NSCalendarDate object uses the locale information to find the names of months and the days of the week for the user’s preferred language.

The Registration Domain

The registration domain defines the set of default values to use if a given preference is not set explicitly in one of the other domains. At launch time, an app can call the registerDefaults: method of NSUserDefaults to specify a default set of values for important preferences. When an app launches for the first time, most preferences have no values, so retrieving them would yield undefined results. Registering a set of default values ensures that your app always has a known good set of values to operate on.

The contents of the registration domain can be set only by using the registerDefaults: method.

Viewing Preferences Using the Defaults Tool

In OS X, the defaults command-line tool provides a way for you to examine the contents of the user defaults database. During app development, you might use this tool to validate the preferences your app is writing to disk. To do that, you would use a command of the following form from the Terminal app:

defaults read <application-bundle-identifier>

To read the contents of the global domain, you would use the following command:

defaults read NSGlobalDomain

For more information about using the defaults tool to read and write preference values, see defaults man page.