MOBILE

iPhone Application Development : Reading and Writing User Defaults (part 1) - Creating Implicit Preferences

7/6/2011 5:38:51 PM

Design Considerations

The dominant design aesthetic of iOS applications is for simple, single-purpose applications that start fast and do one task quickly and efficiently. Being fun, clever, and beautiful is an expected bonus. How do application preferences fit into this design view?

You want to limit the number of application preferences by creating opinionated software. There might be three valid ways to accomplish a task, but your application should have an opinion on the one best way to accomplish it, and then should implement this one approach in such a polished and intuitive fashion that your users instantly agree it’s the best way. Leave the other two approaches for someone else’s application. It may seem counterintuitive, but there is a much bigger market for opinionated software than for applications that try to please everyone.

There are some very important roles for application preferences. Use preferences for the choices your users must make, rather than for all the choices they could possibly make. For example, if you are connecting to the application programming interface (API) of a third-party web application on behalf of your user, and the user must provide credentials to access the service, this is something the user must do, not just something users might want to do differently, so it is a perfect case for storing as an application preference.

Another strong consideration for creating an application preference is when a preference can streamline the use of your application; for example, when users can record their default inputs or interests so that they don’t have to make the same selections repeatedly. You want user preferences that reduce the amount of onscreen typing and taps that it takes to achieve the user’s goal for using your application.

After you decide a preference is warranted, you have an additional decision to make. How will you expose the preference to the user? One option is to make the preference implicit based on what the user does while using the application. An example of an implicitly set preference is returning to the last state of the application. For example, suppose a user flips a toggle to see details. When the user next uses the application, the same toggle should be flipped and showing details.

Another option is to expose your application’s preference in Apple’s Settings application, shown in Figure 14.1. Settings is an application built in to the iPhone. It provides a single place to customize the iPhone. Everything from the hardware, built-in applications from Apple, and third-party applications can be customized from the Settings application.

Figure 14.1. The Settings application.


A settings bundle lets you declare the user preferences of your application so that the Settings application can provide the user interface for editing those preferences. There is less coding for you to do if you let Settings handle your application’s preferences, but less coding is not always the dominant consideration. A preference that is set once and rarely changes, such as the username and password for a web service, is ideal for configuring in Settings. In contrast, an option that the user might change with each use of your application, such as the difficulty level in a game, is not appropriate for Settings.

Reading and Writing User Defaults

Application preferences is Apple’s name for the overall preference system by which applications can customize themselves for the user. The application preferences system takes care of the low-level tasks of persisting preferences to the device, keeping each application’s preferences separate from other applications’ preferences, and backing up application preferences to the computer via iTunes so that users won’t lose their preferences in case the device needs to be restored. Your interaction with the application preferences system is through an easy-to-use API that consists mainly of the NSUserDefaults singleton class.

The NSUserDefaults class works similarly to the NSDictionary class. The main differences are that NSUserDefaults is a singleton and is more limited in the types of objects it can store. All the preferences for your application are stored as key/value pairs in the NSUserDefaults singleton.

A singleton is just an instance of the Singleton pattern, and a pattern in programming is just a common way of doing something. The Singleton pattern is fairly common in iOS, and it is a technique used to ensure that there is only one instance (object) of a particular class. Most often it is used to represent a service provided to your program by the hardware or operating system.


Creating Implicit Preferences

In our first example, we will create a (admittedly ridiculous) flashlight application. The application will have an on/off switch and will shine a light from the screen when it is on. A slider will control the brightness level of the light. We will use preferences to return the flashlight to the last state the user left it in.

Setting Up the Project

Create a new View-Based iPhone Application in Xcode and call it Flashlight. Click the FlashlightViewController.h file in the Classes group and add outlets for our on/off switch, brightness slider, and light source. Add an action called setLightSourceAlpha that will respond when toggling the switch or sliding the brightness control. The FlashlightViewController.h file should read as shown in Listing 1.

Listing 1.
#import <UIKit/UIKit.h>

@interface FlashlightViewController : UIViewController {

IBOutlet UIView *lightSource;
IBOutlet UISwitch *toggleSwitch;
IBOutlet UISlider *brightnessSlider;

}

@property (nonatomic, retain) UIView *lightSource;
@property (nonatomic, retain) UISwitch *toggleSwitch;
@property (nonatomic, retain) UISlider *brightnessSlider;

-(IBAction) setLightSourceAlphaValue;

@end

Next, edit the FlashlightViewController.m implementation file and add corresponding @synthesize directives for each property, following the @implementation line:

@synthesize lightSource;
@synthesize toggleSwitch;
@synthesize brightnessSlider;

To make sure we don’t forget to clean up the retained objects later, edit the dealloc method to release the lightSource, toggleSwitch, and brightnessSlider objects:

- (void)dealloc {
[lightSource release];
[toggleSwitch release];
[brightnessSlider release];
[super dealloc];
}

Now, let’s lay out the UI for the flashlight.

Creating the Interface

Open Interface Builder by double-clicking the FlashlightViewController.xib file in the Resources group.

1.
In Interface Builder, click the empty view and open the Attributes Inspector (Command+1).

2.
Set the background color of the view to black. (We want our flashlight to have a black background.)

3.
Drag a UISwitch from the Library onto the bottom left of the view.

4.
Drag a UISlider to the bottom right of the view. Size the slider to take up all the horizontal space not used by the switch.

5.
Finally, add a UIView to the top portion of the view. Size it so that it is full width and takes up all the vertical space above the switch and slider. Your view should now look like Figure 2.



Figure 2. The Flashlight UI in Interface Builder.

Connect the Outlets and Action

The code we will write to operate the flashlight and deal with the application preferences will need access to the switch, slider, and light source. Control-drag from the File’s Owner icon and connect the lightSource IBOutlet to the new UIView, the toggleSwitch IBOutlet to the UISwitch, and the brightnessSlider IBOutlet to the UISlider.

In addition to being able to access the three controls, our code needs to respond to changes in the toggle state of the switch and changes in the position of the slider. Select the UISwitch and open the Connections Inspector (Command+2). Connect the UISwitch to the setLightSourceAlphaValue method by dragging from the circle beside the Value Changed event to the File’s Owner icon. Choose setLightSourceAlphaValue when prompted to make the connection, as seen in Figure 3.

Figure 3. Connect the switch and slider to the setLightSourceAlphaValue method.

Repeat this process for the UISlider, having its Value Changed event also call the setLightSourceAlphaValue method. This ensures immediate feedback when the user adjusts the slider value.

Save your work, and then switch back to Xcode.

Implementing the Application Logic

What can I say? Flashlights don’t have much logic!

When the user toggles the flashlight on or off and adjusts the brightness level, the application will respond by adjusting the alpha property of the lightSource view. The alpha property of a view controls the transparency of the view, with 0.0 being completely transparent and 1.0 being completely translucent. The lightSource view is white and is on top of the black background. When the lightSource view is more transparent, more of the black will be showing through and the flashlight will be darker. When we want to turn the light off, we just set the alpha property to 0.0 so that none of the white background of the lightSource view will be showing.

To make the flashlight work, we can add this method in Listing 2 to FlashlightViewController.m.

Listing 2.
-(IBAction) setLightSourceAlphaValue {
if (toggleSwitch.on) {
lightSource.alpha = brightnessSlider.value;
} else {
lightSource.alpha = 0.0;
}
}

This simple method checks the on property of the toggleSwitch object, and, if it is on, sets the alpha property of the lightSource UIView to the value property of the slider. The slider’s value property returns a floating-point number between 0 and 100, so this is already enough code to make the flashlight work. You can run the project yourself and see.

Storing the Flashlight Preferences

We don’t just want the flashlight to work; we want it to return to its last state when the user uses the flashlight application again later. We’ll store the on/off state and the brightness level as implicit preferences. First we need two constants to be the keys for these preferences. Add these constants to the top of the FlashlightViewController.h interface file:

#define kOnOffToggle @"onOff"
#define kBrightnessLevel @"brightness"

Then we will persist the two values of the keys in the setLightSourceAlphaValue event of the FlashlightViewController. We will get the NSUserDefault singleton using the standardUserDefaults method and then use the setBool and setFloat methods. Because NSUserDefaults is a singleton, we are not creating it and are not responsible for managing its memory. We will wrap up by using the NSUserDefaults method synchronize to make sure that our settings are stored immediately.

Update the setLightSourceAlphaValue method, as shown in Listing 3.

Listing 3.
-(IBAction) setLightSourceAlphaValue {

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:toggleSwitch.on forKey:kOnOffToggle];
[userDefaults setFloat:brightnessSlider.value forKey:kBrightnessLevel];
[userDefaults synchronize];

if (toggleSwitch.on) {
lightSource.alpha = brightnessSlider.value;
} else {
lightSource.alpha = 0.0;
}
}



It can still be useful to know, however, and the answer is that our preferences are stored in a plist file. If you are an experienced Mac user you may already be familiar with plists, which are used for Mac applications, too. When running on a device, the plist will be local to the device, but when we run our application in the iPhone Simulator, the simulator uses our computer’s hard drive for storage, making it easy for us to peek inside the plist.

Run the Flashlight application in the iPhone Simulator, and then use Finder to navigate to /Users/<your username>/Library/Application Support/iPhone Simulator/<Device OS Version>/Applications. The directories in Applications are generated globally unique IDs, but it should be easy to find the directory for Flashlight by looking for the most recent Data Modified. You’ll see Flashlight.app in the most recently modified directory, and you’ll see the com.yourcompany.Flashlight.plist inside the ./Library/Preferences subdirectory. This is a regular Mac plist file, so when you double-click it, it will open with the Property List Editor application and show you the two preferences for Flashlight.


Reading the Flashlight Preferences

Now our application is writing out the state of the two controls anytime the user changes the flashlight settings. So, to complete the desired behavior, we need to read in and use the preferences for the state of the two controls anytime our application launches. For this, we will use the viewDidLoad method, which is provided for us by Xcode as a commented-out stub, and the floatForKey and boolForKey methods of NSUserDefaults. Uncomment viewDidLoad and get the NSUserDefaults singleton in the same way as before, but this time we will set the value of the controls from the value returned from the preference rather than the other way around.

In the FlashlightViewController.m file in the Classes group, implement viewDidLoad, as shown in Listing 4

Listing 4.
- (void)viewDidLoad {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
brightnessSlider.value = [userDefaults floatForKey:kBrightnessLevel];
toggleSwitch.on = [userDefaults boolForKey:kOnOffToggle];
if ([userDefaults boolForKey: kOnOffToggle]) {
lightSource.alpha = [userDefaults floatForKey:kBrightnessLevel];
} else {
lightSource.alpha = 0.0;
}
[super viewDidLoad];
}


That’s all there is to it. All we need now is a snazzy application icon, and we, too, can make millions in the App Store with our Flashlight application (see Figure 4).

Figure 4. Flashlight application in action.



If you’re running iOS 4 and press the Home button, be aware that your application won’t quit; it will be suspended in the background. To fully test the flashlight app, be sure to use the iOS Task Manager to force the application completely closed, and then verify that your settings are restored when it relaunches fresh.


While we are waiting for the cash to pour in (it may be awhile), let’s look at an application where the user takes more direct control of the application’s preferences.

Other  
  •  - Mobile Application Security : SMS Security - Overview of Short Message Service
  •  - Mobile Application Security : Bluetooth Security - Bluetooth Security Features
  •  Integrating Your Application with Windows Phone 7
  •  Introducing Windows Phone 7 Photo Features (part 2) - Using a Chooser to Open Photos & Saving Photos to the Phone
  •  Introducing Windows Phone 7 Photo Features (part 1) - Using a Chooser to Take Photos
  •  Mobile Application Security : Bluetooth Security - Bluetooth Technical Architecture
  •  Mobile Application Security : Bluetooth Security - Overview of the Technology
  •  Windows Phone 7 Development : Push Notifications - Implementing Cloud Service to Track Push Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Raw Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Tile Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Toast Notifications
  •  iPhone Application Development : Creating a Navigation-Based Application
  •  Windows Phone 7 Development : Push Notifications - Introducing the Push Notifications Architecture
  •  Windows Phone 7 Development : Push Notifications - Understanding Push Notifications
  •  Windows Phone 7 Development : Handling Multiple Concurrent Requests with Rx.NET
  •  WAP and Mobile HTML Security : Application Attacks on Mobile HTML Sites
  •  WAP and Mobile HTML Security : Authentication on WAP/Mobile HTML Sites & Encryption
  •  iPhone Application Development : Displaying and Navigating Data Using Table Views - Building a Simple Table View Application
  •  iPhone Application Development : Understanding Table Views and Navigation Controllers
  •  Windows Phone 7 Development : Revising WeatherRx to Manage Slow Data Connections
  •  
    Top 10
    Nikon 1 J2 With Stylish Design And Dependable Image And Video Quality
    Canon Powershot D20 - Super-Durable Waterproof Camera
    Fujifilm Finepix F800EXR – Another Excellent EXR
    Sony NEX-6 – The Best Compact Camera
    Teufel Cubycon 2 – An Excellent All-In-One For Films
    Dell S2740L - A Beautifully Crafted 27-inch IPS Monitor
    Philips 55PFL6007T With Fantastic Picture Quality
    Philips Gioco 278G4 – An Excellent 27-inch Screen
    Sony VPL-HW50ES – Sony’s Best Home Cinema Projector
    Windows Vista : Installing and Running Applications - Launching Applications
    Most View
    Bamboo Splash - Powerful Specs And Friendly Interface
    Powered By Windows (Part 2) - Toshiba Satellite U840 Series, Philips E248C3 MODA Lightframe Monitor & HP Envy Spectre 14
    MSI X79A-GD65 8D - Power without the Cost
    Canon EOS M With Wonderful Touchscreen Interface (Part 1)
    Windows Server 2003 : Building an Active Directory Structure (part 1) - The First Domain
    Personalize Your iPhone Case
    Speed ​​up browsing with a faster DNS
    Using and Configuring Public Folder Sharing
    Extending the Real-Time Communications Functionality of Exchange Server 2007 : Installing OCS 2007 (part 1)
    Google, privacy & you (Part 1)
    iPhone Application Development : Making Multivalue Choices with Pickers - Understanding Pickers
    Microsoft Surface With Windows RT - Truly A Unique Tablet
    Network Configuration & Troubleshooting (Part 1)
    Panasonic Lumix GH3 – The Fastest Touchscreen-Camera (Part 2)
    Programming Microsoft SQL Server 2005 : FOR XML Commands (part 3) - OPENXML Enhancements in SQL Server 2005
    Exchange Server 2010 : Track Exchange Performance (part 2) - Test the Performance Limitations in a Lab
    Extra Network Hardware Round-Up (Part 2) - NAS Drives, Media Center Extenders & Games Consoles
    Windows Server 2003 : Planning a Host Name Resolution Strategy - Understanding Name Resolution Requirements
    Google’s Data Liberation Front (Part 2)
    Datacolor SpyderLensCal (Part 1)