User defaults
is another
term for user
application preferences. Mac OS X
has a well-designed user defaults system that is accessed in Cocoa
through the Foundation class
NSUserDefaults
. Working with
NSUserDefaults
is similar to working with an
NSDictionary
. Default values are stored in the
database by keys that the application developer defines in the
application. The defaults database is actually a collection of
property list files; every application has its own property list file
where defaults are stored. You can view these files in
~/Library/Preferences.
Defaults are organized into
domains
,
which are groupings of default values that have varying degrees of
visibility to applications. A domain is either
persistent
or
volatile
.
Defaults in a persistent domain are stored in the
defaults database, while defaults in a volatile domain are applicable only
during the lifetime of the NSUserDefaults
object
that contains those values. NSUserDefaults
has
five standard domains:
NSArgumentDomain
Set values for defaults in the argument domain by passing key-value
pairs to the application as arguments on the command line, (e.g.,
% MyApp -KeyName
Value
). The argument domain is volatile,
so arguments affect the application only during the application
session for which they were specified.
Application-specific defaults are stored here and kept persistently in the user’s defaults database.
NSGlobalDomain
Defaults stored in the global domain are applicable to all applications run by the user. This persistent domain is stored in the defaults database.
The languages domain stores defaults that pertain to language choice and localization.
NSRegistrationDomain
The registration domain is the lowest-level domain containing application-provided defaults (or “factory settings”) used when a default value is otherwise unspecified in a higher domain.
When a default is requested, the domains are searched for the value
in order, starting with NSArgumentDomain
and
ending with NSRegistrationDomain
. The search ends
at the first discovery of a default value. Thus, if many domains have
values for the same default, NSUserDefaults
returns the default that occurred in the higher-level domain. You can
exploit the search order as a debugging aid by overriding any default
by specifying a value in the NSArgumentDomain
.
User defaults are capable of storing only what property lists can
store, namely NSData
, NSNumber
,
NSString
, NSDate
,
NSArray
, and NSDictionary
(although convenience methods are also provided to get and set scalar
values). Using these data types, you can store information, such as
dates, numbers, and text, as well as any object that is archiveable.
Example 2-33 shows how to interact with the user
defaults system.
Example 2-33. Interacting with the user defaults system using NSUserDefaults
// Create an instance of NSUserDefaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];// Store and retrieve a string
[prefs setObject:@"Mike Beam" forKey:@"Author"]; NSString *author = [prefs stringForKey:@"Author"];// Store and retrieve a number
[prefs setFloat:1373.50 forKey:@"NASDAQ"]; [prefs setInt:2002 forKey:@"Year"]; float level = [prefs floatForKey:@"NASDAQ"]; int year = [prefs intForKey:@"Year"];// Store and retrieve dates
[prefs setObject:[NSDate date] forKey:@"Last Opened"]; NSDate *lastOpenDate = [prefs objectForKey:@"Last Opened"];// Store collections
[prefs setObject:dictionary forKey:@"A Dictionary"]; [prefs setObject:array forKey:@"An Array"];// Retrieve collections
NSArray *array = [prefs arrayForKey:@"An Array"]; NSDictionary *dict = [prefs dictionaryForKey:@"A Dictionary"];// Use the following if you want mutable objects...
NSMutableArray *mArray = [NSMutableArray arrayWithArray: [prefs arrayForKey:@"An Array"]]; NSMutableDictionary *mDict = [NSMutableDictionary dictionaryWithDictionary: [prefs dictionaryForKey:@"A Dictionary"]];
All applications should establish factory default settings in the
registration domain. This is done with the
registerDefaults
method. Establishing defaults in
the registration domain often takes place in an overridden
initialize
class method of one of the first
classes loaded in your application. This method works well because it
is used to initialize classes when they are first loaded by the
runtime system, and it is thus one of the earliest entry points in
code execution. This example shows how it might be done for a small
number of defaults:
+ (void)initialize { NSUserDefaults *prefs; NSMutableDictionary *defs; prefs = [NSUserDefaults standardUserDefaults]; [defs setObject:@"May" ForKey:@"Month"]; [defs setInteger:2002 ForKey:"Year"]; [prefs registerDefaults:defs]; }
If you need to register a large number of defaults, hardcoding them this way might prove cumbersome. For these situations, it may be more convenient to store factory settings in a property list included with the application, which is then read into a dictionary and registered with user defaults:
+ (void)initialize { NSString *prefsFile; NSUserDefaults *prefs; NSDictionary *defs;// The factory defaults file is a resource in the application
// bundle. The path is retrieved using NSBundle.
prefsFile = [[NSBundle mainBundle] pathForResource:@"FactoryDefaults" ofType:@"plist"]; defs = [NSDictionary dictionaryithContentsOfFile:prefsFile]; prefs = [NSUserDefaults standardUserDefaults]; [prefs registerDefaults:defs]; }
One commonly stored preference is an
NSColor
. There are, however, no provisions for
storing a color directly in the defaults database. One way to store
information about colors in the defaults database is to store the
color space name and a dictionary of the color component values.. All
of these data types are supported by
NSUserDefaults
. A better solution is to archive
the NSColor
object into an
NSData
instance and store it in the preferences,
as shown in Example 2-34.
Example 2-34. Storing an NSColor to user defaults
// Assume NSColor object color exists
// Store the color
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *colorData; colorData = [NSArchiver archivedDataWithRootObject:color]; [prefs setObject:colorData forKey:@"Text Color"];// Retrieve the color
colorData = [prefs dataForKey:@"Text Color"]; id color = [NSUnarchiver unarchiveObjectWithData:colorData];
This technique is not limited to only NSColor
. It
can work with any class of object that conforms to the
NSCoding
protocol, the prerequisite for
compatibility with Foundation’s archiving system.