Channels ▼
RSS

Mobile

The iPhone Isn't Easy


Saving State

Being able to halt the app's physics calculations also served another valuable purpose. Suppose a phone call interrupts the app? In this case, the calculations and updates must be stopped while the user decides whether to take the call. If she answers the call, the iPhone OS warns the app that it is going to be shut down. The app receives an "applicationWillTerminate" message, and in response it must preserve its critical state variables. If this is done properly, when the app is relaunched it restores its state and can resume where it left off.

There are a number of ways to store an app's state. Cocoa Touch offers archiving features that allow you to save data and objects. The most comprehensive archiving mechanism is NSCoding, which lets you save the complete object graph of a group of objects. NSCoding preserves objects into nib files, and when they are later reloaded, the complete state of the archived objects is restored. You got a glimpse of this capability when working with views in IB. The UIKit class documents specify upfront whether each class implements NSCoding. The problem is that you must write NSCoding methods to preserve and restore your custom-made class. That seemed a lot of work, particularly when my app can be easily reconstituted by preserving just a few variables.

Cocoa Touch also offers a serialization mechanism that lets you store certain data objects. The serialized data is stored as a property list, which could be part of the application's property list (the Info.plist file) or stored as user preferences, using NSUserDefaults. I chose NSUserDefaults because it was simple to set up and met my needs.

With NSUserDefaults, you stash the data into a shared data object, using a key (a string) to uniquely identify it. You also use key names to both identify and access the variables you store within the object. The methods I wrote to store and retrieve the variables are shown in Listing Seven.

Listing Seven

- (void)saveState {
	NSUserDefaults *savedData = [NSUserDefaults standardUserDefaults];
	[savedData setDouble:shipHeading forKey:@"shipHeading"];
	[savedData setDouble:shipX forKey:@"shipX"];
	[savedData setDouble:shipY forKey:@"shipY"];
	[savedData setDouble:mDX forKey:@"mDX"];
	[savedData setDouble:mDY forKey:@"mDY"];	
	[savedData synchronize];
}

- (void)restoreState {
            NSUserDefaults *savedData = [NSUserDefaults standardUserDefaults];
           int programStateSaved = [savedData integerForKey:@"programStateSaved"]; // Get saved state flag
	
	if (programStateSaved) {
		shipHeading = [savedData doubleForKey:@"shipHeading"]; // Get heading
		shipX = [savedData doubleForKey:@"shipX"]; // Get X coord
		shipY = [savedData doubleForKey:@"shipY"]; // Get Y coord
		mDX = [savedData doubleForKey:@"mDX"]; // Get X velocity
		mDY = [savedData doubleForKey:@"mDY"]; // Get Y velocity
	} else {												// First run
		[savedData setInteger:1 forKey:@"programStateSaved"];	// Create save flag
		shipX = self.view.center.x;
		shipY = self.view.center.y;
		mDX = 0.0;
		mDY = 0.0;
		shipHeading = 0.0;
	}
}

Saving data in the user preferences object is straightforward. You retrieve a pointer to access the object, and save the critical velocity, position, and bearing information tagged with descriptive key strings. This code is invoked when the UIApplicationDelegate object receives the applicationWillTerminate message.

Retrieving the stored data is easy, and its method is called from the delegate's applicationDidFinishLaunching method. The code first checks for a flag, programStateSaved, to see if the app's stored data exists. If not, it makes such a flag and stores some default values into the object. If the flag exists, then the code uses the same keys used to store the data to retrieve it. The code places these values into the appropriate variables. Now all that's left to do is have gameLoop resume execution, and the physics engine picks up where it left off.

Finally, because of the iPhone's OS X underpinnings, you have a third option in which to save the app's context. You can use UNIX BSD-style file system calls to store and read the app's state, rather than Cocoa Touch. A good example of this technique appears in Wolfenstein 3D, where its SaveTheGame method uses a passel of fwrite() calls to save the game's critical variables, and its LoadTheGame method uses fread() calls to restore the game's state.

Final Polish

Getting a working app isn't enough, however. There are other design details that need to be dealt with. The first was checking the usability of the interface that I'd cobbled together. For that, I sought the feedback of a beta-tester, which was my son, John. While waiting in line for Avatar, I handed him the iPhone and had him fire up the app and fool with it. His feedback was swift and sobering: The control buttons were too small, and they needed to better indicate their function. I had drawn some crude arrows for the control pad and obviously they were lacking.

While I have some graphic arts skills, the app's UI would fare far better if I found some professionally designed arrow images on the Web. Ideally, they should be in PNG format (the iPhone's preferred image format, although it can handle JPEG, TIFF, GIF, Windows BMP, and XWindows bitmap files), and with a transparent background. I wasn't asking for much, was I?

After a little scouting, I found the Icons web page, which is part of the MySiteMyWay site. It provides a vast array of attractive icons in different colors and designs. There are icons for all sorts of images and concepts, and they are 512 x 512 pixels in size with transparent backgrounds. You can use any pixel-editing program to scale them down for the iPhone, although the original size might be handy for iPad app interfaces. Best of all, they're free!

I downloaded the glossy silver icons archive, and used Lemke Software's Graphic Converter X to edit the images. I selected several arrow images to represent the various control pad actions, scaled them down, and then merged them together into one image. This became the control pad displayed on the screen (Figure 4).

Figure 4

To complete the app, two other images had to be made. The first was a 320- by 480-pixel PNG image that's stored in a file named Default.png. When the iPhone app launches, this image is loaded automatically as a background scene until your app initializes and adds its views to the window. Otherwise, the user becomes alarmed when they stare at a blank screen for more than several seconds. A properly designed default image shows the user that the application is starting up and doing things. You can see this behavior in Apple's weather app, in that it first displays the blank days of the week graphic while it fetches weather information. For simple apps, this default image could be either a splash screen, or present a message that states the app is loading. More complex apps with longer load times should display a progress bar.

I had used Blender3D, an open source 3D modeling program, to generate the spacecraft image for Android. I had the original model file handy, so I used Blender3D render a larger image for use as the splash screen. Next, I had Blender3D render the ship into 57 x 57 pixel image file named Icon.png. This image serves as the application icon. I added these two files to the XCode Space project and after rebuilding the app, the icon and splash screen appeared with no further coding effort on my part. My tester, John, was much happier with the UI.

Lessons Learned

Looking back, there are a few things I would have done differently. For example, for my info button I use a graphic, but late in the design I learned that the there are two button types, UIButtonTypeInfoLight and UIButtonTypeInfoDark, that display the info icon for you. In addition, I'd group the initialization of the control pad and info button views into one method, and probably do the same for updating them. Contrary to how things are done on a desktop application, where one updates the UI as things happen, on a mobile platform it's better to group the graphics redraws together into bursts that allow the processor to fall idle and converse battery life. The reason for the separate code for these views is that my app grew incrementally as I learned things. Finally, the simple update code just hammers out redraw messages in response to touches, particularly for the ship's engine firing. It might be worth investigating if a little logic could implement smarter screen updates that occur only when something really changes.

Looking ahead, improvements that I'd like to make to Space are its ability to handle a change in screen orientation. Also, I'd like to make the screen controls configurable. I'm left-handed and put the control pad in the iPhone's left corner, but I realize my handedness is in the minority and other uses might want the control pad on the right. I'd also like to add guard logic so that the app could react if critical image files were missing, or if the saved variables were corrupt.

However, as such my iPhone app shows that I gotten off to a good start on my journey. I now understood Cocoa Touch's View classes sufficiently to draw to the screen, display controls, handle finger taps, and update the display in response to them. In addition, the app saves and restores its state when shut down, or when I take a call. The program accomplishes all of this using little code, which speaks well for the capabilities of Cocoa Touch and the iPhone mobile platform.


Tom Thompson is an experienced developer for multiple mobile platforms. He can be contacted at tom_thompson@lycos.com.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video