Learning Cocoa with Objective-C/Miscellaneous Topics/Defaults and Preferences

From WikiContent

< Learning Cocoa with Objective-C | Miscellaneous Topics
Revision as of 12:56, 7 March 2008 by Docbook2Wiki (Talk)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search
Learning Cocoa with Objective-C

Most applications need to store and retrieve preferences that allow for user customization of an application's behavior and keep track of configuration settings. Mac OS X provides, as part of its Core Foundation framework, a preferences system that provides a simple and standard way to maintain these preferences. Cocoa calls these preferences defaults.

If you go to the ~/Library/Preferences folder, you will see the user-preferences database used by the applications you run on your system. In fact, many of the applications we have created in this book have written preferences to this database. Take a look, and you'll see that you probably have Dot View.plist, Menu.plist, and RTF Edit.plist files. These contain preferences used by some of the various Cocoa base classes used in the sample applications we've built throughout the book.

In this chapter we'll show you how to take advantage of the user-preferences system from your applications using Cocoa.

Contents

How Preferences Work

The preference system in Mac OS X allows you to store values associated with a key—the name of a property—that can later be used to look up the preference value when you need it. These key-value pairs are scoped using a combination of username and application ID. All of the preferences for a user are stored in his ~/Library /Preferences folder.

There are multiple domains—or different scopes of coverage—in which a preference can exist. When you request a preference, the following resources are searched—in order—until a match is found:

  1. The list of arguments that were passed to an application. This lets an application start up with a preference setting to override all other values for that preference's key.
  2. The users preferences stored in the ~/Library/Preferencesfolder. This is where preferences that are unique to a user and that need to last between invocations of an application are kept.
  3. A set of global preferences used across all applications that a user may use. For example, rulers in text views will obtain a user's preferred unit of measurement. These preferences are stored in the ~/Library/Preferences/GlobalPreferences.plistfile. Many of these preferences are set using the System Preferences application.
  4. The set of preferences that your application registers as the defaults for your application. If a value for a preference is not found anywhere else, then this allows your application to provide a default value.

Preferences in the ~/Library/Preferencesfolder are stored in the same property-list file format used by the Info.plist file included with application bundles. Preference value objects can be of any of the following types:

  • NSString for storing string values
  • NSNumber for storing number values derived from integers, floats, and other numeric types
  • NSBoolean for storing YES or NO values
  • NSDate for storing date information
  • NSData for storing arbitrary data
  • NSArray for storing arrays that consists of any of the previously listed types
  • NSDictionary for storing name/value dictionaries that have values of any of the previously listed types

Warning

While it is possible to edit the files in your ~/Library/Preferences folder yourself using any text editor, you probably should not. You might introduce accidental errors into the XML syntax of the files. We'll see some other ways to edit these files in just a bit.

Using Defaults

To show how to use the NSUserDefaults class, the mechanism by which Cocoa provides access to Mac OS X's preference system, we'll build a simple application to keep track of a few of our favorite things.

  1. Create a new Cocoa application project in Project Builder (File → New Project → Application → Cocoa Application) named "Favorites", and save it in your ~/LearningCocoafolder.
  2. Open the MainMenu.nib file in Interface Builder.
  3. Lay out the user interface as shown in Figure 15-1.

    Figure 15-1. Favorites application interface

    Favorites application interface

  4. Create a subclass of NSObject in Interface Builder. Click on the Classes tab of the MainMenu.nib window, find NSObject, Control-click it, and select Subclass NSObject from the pop-up menu. Name the subclass "Controller".
  5. Create the following outlets on the Controller class:
    • bookField
    • colorField
    • foodField
    • cityField
  6. Create an action named textFieldChanged:. This action will tell the preferences database when an item has changed.
  7. Generate the source-code files for the Controller class (Classes → Create Files for Controller).
  8. Instantiate the Controller class (Classes → Instantiate Controller).
  9. Connect the four text fields on the user interface to their respective outlets by control-dragging a connection from the Controller instance to each of the fields in turn.
  10. Connect each of the four text fields to the Controller's textFieldChanged action method by control-dragging a connection from the text field to the Controller instance.
  11. Save (File → Save, or [[Image:Learning Cocoa with Objective-C_I_4_tt541.png|]] -S) the nib file, and return to Project Builder.
  12. Edit the Controller.hfile as shown, adding an instance variable that will hold a reference to a NSUserDefaults object.
    #import <Cocoa/Cocoa.h>
    
    @interface Controller : NSObject
    {
        IBOutlet id bookField;
        IBOutlet id cityField;
        IBOutlet id colorField;
        IBOutlet id foodField;
        NSUserDefaults * prefs;
    }
    - (IBAction)textFieldChanged:(id)sender;
    @end
    
  13. Add an init method to the Controller.m file. This will set the prefs instance variable.
                         - (id)init
                         {
                             [super init];
                             NSMutableDictionary * defaultPrefs = [NSMutableDictionary dictionary];   // a
    
                             [defaultPrefs setObject:@"Learning Cocoa" forKey:@"FavBook"];            // b
                             [defaultPrefs setObject:@"San Francisco" forKey:@"FavCity"];               
                             [defaultPrefs setObject:@"Red" forKey:@"FavColor"];                        
                             [defaultPrefs setObject:@"Mexican" forKey:@"FavFood"];                     
    
                             prefs = [[NSUserDefaults standardUserDefaults] retain];                  // c
                             [prefs registerDefaults:defaultPrefs];                                   // d
    
                             return self;    
                         }
                      
    

    This code does the following things:

    1. Creates a new mutable dictionary that will serve as the container for the default values the application will use
    2. Sets four key/value pairs that correspond to the default values we want to store in the preferences system
    3. Obtains a reference to the preferences system
    4. Indicates to the prefs object that we want to use the defaultPrefs dictionary as the set of default preferences
  14. Add a dealloc method so that the class cleans up after itself properly.
                         - (void)dealloc
                         {
                             [prefs release];
                             [super dealloc];
                         }
                      
    
  15. Add an awakeFromNib method to populate the user interface from any settings that are in the prefs object.
                         - (void)awakeFromNib
                         {
                             [bookField setStringValue:[prefs stringForKey:@"FavBook"]];
                             [cityField setStringValue:[prefs stringForKey:@"FavCity"]];
                             [colorField setStringValue:[prefs stringForKey:@"FavColor"]];
                             [foodField setStringValue:[prefs stringForKey:@"FavFood"]];
                         }
                      
    
  16. Implement the textFieldChanged: action method so that the key values are saved as they change to the prefs object.
    - (IBAction)textFieldChanged:(id)sender
    {
        if (sender == bookField) {
                                 [prefs setObject:[bookField stringValue] forKey:@"FavBook"];
                             } else if (sender == cityField) {
                                 [prefs setObject:[cityField stringValue] forKey:@"FavCity"];
                             } else if (sender == colorField) {
                                 [prefs setObject:[colorField stringValue] forKey:@"FavColor"];
                             } else if (sender == foodField ){
                                 [prefs setObject:[foodField stringValue] forKey:@"FavFood"];
                             }
    }
    
  17. Build and run ([[Image:Learning Cocoa with Objective-C_I_4_tt547.png|]]-R) the application. You should see the interface launch as shown in Figure 15-2.

    Figure 15-2. Our application running

    Our application running

  18. Change some of the values. Quit the application, then restart to see if the values were saved.
  19. Find the Favorites.plist file in your ~/Library/Preferencesfolder. Double-click the file to launch the Property List Editor (/Applications/Utilities), which allows you to see the values that you changed in the file, as shown in Figure 15-3. To see the XML representation of the plist file, click the Dump button. You can also, if needed, edit the property list here, save the changes, and then launch the application we built to see the changes.

    Figure 15-3. The preferences file under the hood

    The preferences file under the hood


Command-Line Preferences Access

You can access your preferences from the Terminal by using the defaults command. For example, to see the defaults for the Favorites application we just built, type the following into a Terminal window:

               defaults read Favorites
            

Something like the following should print:

{
    FavBook = "Stranger in a Strange Land"; 
    FavCity = "Dallas Texas"; 
    FavColor = Black; 
    FavFood = Thai; 
}

If you want to modify a preference from the command line, you can use the defaultscommand's write option. For example, to change our favorite city to Sedona, Arizona, issue the following command (the quotes are needed to accommodate the comma in the value string we are using):

               defaults write Favorites FavCity "Sedona, Arizona"
            

Now go back to Project Builder, and build and run ([[Image:Learning Cocoa with Objective-C_I_4_tt555.png|]]-R) the Favorites application again to see the changes.

Using Unique Application Identifiers

As we said before, preferences are stored in files using an application's ID. So far, our sample applications haven't specified a particular application ID, so the system has used the name of the application as its ID.

However, when you browse through your ~/Library/Preferences folder, you'll see that quite a few of the files have long names like com.apple.iPhoto.plist. When Apple created the iPhoto application, they assigned it an application ID of com.apple.iPhoto—Apple's domain name followed by the application name—to reduce the possibility of somebody else's application named iPhoto interfering with the preferences used by the system. Obviously, for an application like iPhoto, there probably won't be a collision. But for applications with more common names—like the ones that we have been creating in this book—this level of namespace protection is valuable.

For example, all of the Mac OS X applications created by O'Reilly & Associates should have application IDs that start with com.oreilly. Following this logic, our Favorites application should have an application ID of com.oreilly.Favorites. To specify this application ID:

  1. Open up the main target of the application, navigate the outline view to Info.plist Entries → Simple View → Basic information, and change the Identifier field, as shown in Figure 15-4.

    Figure 15-4. Setting the application identifier

    Setting the application identifier

  2. Build and run ([[Image:Learning Cocoa with Objective-C_I_4_tt557.png|]]-R) the application, change the values, and quit the application.
  3. Using the Finder, you can see that there is now a com.oreilly.Favorites.plistfile in your ~/Library/Preferencesfolder. If you wanted to read this file from the command line, you could use the following command:
    defaults read com.oreilly.Favorites
    

Exercises

  1. Take a look at the contents of the Favorites preference list file using Property List Editor.
  2. Add a reset button to the Favorites application that will reset the values to their original state.
  3. Modify the Favorites application so that it reads its application defaults from a .plist file contained as a resource in its application bundle.
  4. Modify the Currency Converter application from Chapter 5 to remember the exchange rate between invocations of the application.
Personal tools