25. Settings

Many applications include preferences that users can customize. Whether users are picking the size of the text or storing passwords, there is a standard way of enabling iPhone application preferences. In this chapter, you will use the NSUserDefaults class to add a preference to your Nayberz application. This preference will specify the message that you publish in your net service (Figure 25.1).

Figure 25.1. Nayberz Settings pane

image

Settings Bundle

In the top-level directory of an iPhone application bundle, the filename Settings.bundle is reserved for a directory to contain the application’s preference settings. To add preferences to your application, you add a Settings.bundle directory to your application. This directory must contain a file, Root.plist, where you set key-value pairs to define the application preferences. Once you’ve done this, your application will have its own pane in the Settings application where the user can set these preferences.

XCode makes it very easy to add a Settings.bundle to an application. Reopen Nayberz.xcodeproj. From the File menu, select New File.... In the table on the left side, under the iPhone OS group, select Resource. Then in the table on the right side, select Settings Bundle and click Next. The next screen will let you change the File Name and Location, but don’t change them. Just be sure that the Add to Project: field is set to Nayberz and Targets: Nayberz is checked before you click Finish.

The Settings.bundle will now appear in your project’s Groups & Files table. Use the disclosure buttons to reveal its contents, Root.plist and en.lproj. The en.lproj directory contains a strings table called Root.strings. The file en.lproj/Root.strings contains the English version of the text that will appear in the Nayberz pane of Settings. This means you can localize Root.strings.

Double-click on Root.plist to open it in the Property List Editor. You will see that it contains two keys at the root level: StringsTable and PreferenceSpecifiers. The value of the StringsTable key (Root) gives the name of the strings table file (Root.strings) that will be found in each localization directory. A value for this key is not required but your application’s settings will not be localized without one.

Right now, you are interested in the other key, PreferenceSpecifiers. The value of this key is an array of dictionaries. Each of these dictionaries defines a preference specifier. If you expand the PreferenceSpecifiers key in the Property List Editor, you will see the preference specifier dictionaries. These “sample” preferences were added by XCode when it created the Settings.bundle for you. If you expand these dictionaries, you will see the key-value pairs that define each of the preferences. Build and run Nayberz with these sample preferences. After your application starts, exit it. Then open Settings and scroll down to see Nayberz in the list. Select it and compare the key-value pairs in the dictionaries to the appearance of the properties in Settings. Pretty cool, huh?

Each preference specifier defines an attribute in Settings using key-value pairs. Here we’ll discuss three of those keys: Type, Key, and DefaultValue. (Yeah, I know. One of the keys is actually named Key.)

The value for Type is one of seven constants that specifies the widget type used for this preference in Settings:

image

Note that PSGroupSpecifier and PSChildPaneSpecifier do not specify actual preferences; they control the grouping and paging of preferences in Settings.

The value for Key is a string to be associated with the preference. Each of these strings must be unique within your PreferenceSpecifiers. Your application code will use this key to reference that preference.

The value for DefaultValue is the “factory setting” – the value of the preference when the user first downloads the application. This value must be of the type that the preference specifier expects (e.g., string for PSTextFieldSpecifier).

Depending on the type of the preference specifier, there may be other keys available to configure the appearance of the associated widget. Some of these keys are required, some are not. The documentation of the preference types and the available keys for each of those types is in the “Settings Application Schema Reference” in the documentation.

Now you are going to delete the sample preferences and replace them with a single new preference. This preference will have the key BNRMessagePrefKey, and it will specify the user’s preference for the message broadcast by Nayberz. The factory setting will be “I love you all.” If you double-click on Root.plist, it will open in the Property List Editor (and the XML will be hidden). In this editor, you could add and remove items and edit their values. In this case, however, you’re going to edit the XML file directly. Right-click on Root.plist. Select Open As from the pop-up menu and then select Source Code File. Remove the existing preferences and replace them with the single preference shown below.

image

Save the file. You have just created a text field in the Nayberz pane of Settings where the user can set a personal value for the BNRMessagePrefKey preference. Here you only created a single preference, but there could be many in a complex application. Remember that each preference needs a unique key, and it is best to be verbose with these key names. Here you have a prefix (“BNR”, for Big Nerd Ranch), a name (“Message”), and the suffix “PrefKey”. This way, no one will get confused about what the keys in your code mean.

Save Root.plist and build and run your application. After your application starts up, exit it. Open Settings and scroll down to find Nayberz. Select it and set your preference for the Nayberz message.

NSUserDefaults

Now the user can set the preference for BNRMessagePrefKey. But you still need to write a little code to respect the user’s preference in your application. The NSUserDefaults class makes this very simple. It has a class method standardUserDefaults that returns the singular instance of NSUserDefaults for your application. NSUserDefaults is essentially an NSMutableDictionary with keys and values. Its keys are the keys for your preference specifiers in Root.plist, and its values are the user’s settings for those preferences.

Registering defaults

Every time the application launches, you will need to remind it what the factory defaults are by “registering” your factory defaults using the DefaultValues from your Settings.bundle. To register your defaults, you build an NSDictionary containing a key-value pair for each preference. Each key will be a preference key, and its value will be the corresponding DefaultValue for the preference. Then you send this dictionary to the instance of NSUserDefaults using the registerDefaults: message.

The priority of the various defaults settings is as follows:

1. preferences set by the user in Settings (highest)

2. default values set in Root.plist in the Settings.bundle

3. default values set with registerDefaults: (lowest)

So if the user has already set a preference, it will override the other default values.

Your application needs to register its defaults before using the instance of NSUserDefaults to get the user defaults. So you’re going to register them in the initialize method. In initialize in NayberzAppDelegate.m, load the Root.plist in the settings bundle and pull out the default values for every key. Note that initialize is a class method (denoted with a “+”):

image

This generic implementation is a bit of overkill for our single preference, but you will definitely want this snippet of code when you write an application that has multiple preferences.

Using the defaults

Now you are ready to use NSUserDefaults to get the user’s recorded preference for the message. Open NayberzAppDelegate.m and edit the application:didFinishLaunchingWithOptions: method:

image

Respecting changes in suspended applications

Now, in order to change this message, a user must quit the application, open the Settings application, change the value and re-launch Nayberz. However, we know that, as of iOS 4.0, when an application quits, it really just gets suspended. A suspended application does not get sent the message application:didFinishLaunchingWithOptions: when it is transitioned back to the active state, and that is where the TXT record of the net service is configured.

Therefore, Nayberz must somehow find out that its settings were changed externally when it is transitioned out of the suspended state. When an application is suspended, the operating system queues up any information the application may want to know about when it is activated. This information includes things like the battery level, device orientation, proximity state, current locale and preferences. If one of these items changes while an application is suspended, the application is informed when it becomes active through the notification center.

In order to catch these events, you must register observers in the notification center for any changes you are interested in. In this case, you are interested in the preferences changing. Add the following code near the end of application:didFinishLaunchingWithOptions: in NayberzAppDelegate.m.

image

Now, when the user temporarily exits the application to change the BNRMessagePrefKey, Nayberz's notification center will post the NSUserDefaultsDidChangeNotification when re-launched. The block implemented for this notification will immediately update the netService with its new TXT record.

Note that the changes the operating system keeps track of for a suspended application are coalesced into a single message for each type of event. Therefore, if the user defaults change more than once while an application is suspended, the application is only notified once when activated again. For notifications like orientation changes, a single notification is posted to the notification center containing the net result of all orientation changes since the application was last active. A device that rotates from portrait, to landscape, back to portrait and then back to landscape again is only sent one UIDeviceOrientationChangedNotification notification, containing the current orientation of the device: landscape.

Users can now set their own messages, and Nayberz is complete. You don't have to do it this way, though. You could write your own settings interface within your application and not register preference specifiers in a settings bundle. Then, only your application could change the preferences. Or you could do both: write an interface and set up a Settings.bundle. The two ways will respect each other as long as you use NSUserDefaults.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset