Single-click PersistenceViewController.m, and add the following code at the beginning of the file:
#import “PersistenceViewController.h”
@implementation PersistenceViewController
@synthesize field1;
@synthesize field2;
@synthesize field3;
@synthesize field4;
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
- (void)applicationWillTerminate:(NSNotification *)notification { NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:field1.text];
[array addObject:field2.text];
[array addObject:field3.text];
[array addObject:field4.text];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
#pragma mark -
- (void)viewDidLoad {
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
field1.text = [array objectAtIndex:0];
field2.text = [array objectAtIndex:1];
field3.text = [array objectAtIndex:2];
field4.text = [array objectAtIndex:3];
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
[super viewDidLoad];
} ...
Also, insert the following code into the existing dealloc and viewDidUnload methods:
...
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.field1 = nil;
self.field2 = nil;
self.field3 = nil;
self.field4 = nil;
[super viewDidUnload];
}
- (void)dealloc { [field1 release];
[field2 release];
[field3 release];
[field4 release];
[super dealloc];
} ...
The first method we added, dataFilePath, returns the full pathname of our data file by finding the Documents directory and appending kFilename to it. This method will be called from any code that needs to load or save data.
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
The second new method is called applicationWillTerminate:. Notice that it takes a pointer to an NSNotification as an argument. applicationWillTerminate: is a notifica- tion method, and all notifications take a single NSNotification instance as their argument.
A notification is a lightweight mechanism that objects can use to communicate with each other. Any object can define one or more notifications that it will publish to the application’s notification center, which is a singleton object that exists only to pass these notifications between objects. Notifications are usually indications that some event occurred, and objects that publish notifications include a list of notifications in their documentation. For example, if you look at Figure 11-4, you can see that the UIApplication class publishes a number of notifications.
Download at Boykma.Com
Figure 11-4. UIApplication documentation lists all the notifications that it publishes.
The purpose of most notifications is usually pretty obvious from their names, but the documentation contains further information if you find one whose purpose is unclear. Our application needs to save its data before the application quits, so we are interested in the notification called UIApplicationWillTerminateNotification. In a minute, when we write our viewDidLoad method, we will subscribe to that notification and tell the notifica- tion center to call this method when that notification happens:
- (void)applicationWillTerminate:(NSNotification *)notification { NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:field1.text];
[array addObject:field2.text];
[array addObject:field3.text];
[array addObject:field4.text];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
The method itself is fairly simple. We create a mutable array, add the text from each of the four fields to the array, and then write the contents of that array out to a property list file.
That’s all there is to saving our data using property lists.
In the viewDidLoad method, we do a few more things. The first thing we do is check to see if a data file already exists. If there isn’t one, we don’t want to bother trying to load it. If the file does exist, we instantiate an array with the contents of that file and then copy the objects from that array to our four text fields. Because arrays are ordered lists, by copying them in the same order as we saved them, we are always sure to get the right values in the right fields.
- (void)viewDidLoad {
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
field1.text = [array objectAtIndex:0];
field2.text = [array objectAtIndex:1];
field3.text = [array objectAtIndex:2];
field4.text = [array objectAtIndex:3];
[array release];
}
After we load the data from the property list, we get a reference to our application instance and use that to subscribe to the UIApplicationWillTerminateNotification, using the default NSNotificationCenter instance and a method called addObserver :selector:name:object:. We pass an observer of self, which means that our
PersistenceViewController is the object that needs to get notified. For selector, we pass a selector to the applicationWillTerminate: method we wrote a minute ago, tell- ing the notification center to call that method when the notification is published. The third parameter, name:, is the name of the notification that we’re interested in receiving, and the final parameter, object:, is the object we’re interested in getting the notification from. If we pass nil for the final parameter, we would then get notified any time any method posted the UIApplicationWillTerminateNotification.
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
After subscribing to the notification, we just give our superclass a chance to respond to
viewDidLoad, and we’re done.
[super viewDidLoad];
}
That wasn’t too bad, was it? When our main view is finished loading, we look for a property list file. If it exists, we copy data from it into our text fields. Next, we register to be notified when the application terminates. When the application does terminate, we gather up the
Download at Boykma.Com
values from our four text fields, stick them in a mutable array, and write that mutable array out to a property list.
Why don’t you compile and run the application? It should build and then launch in the simulator. Once it comes up, you should be able to type into any of the four text fields.
When you’ve typed something in them, press the home button (the circular button with the rounded square in it at the bottom of the simulator window). It’s very important that you press the home button. If you just quit the simulator, that’s the equivalent of force quitting your application, and you will never receive the notification that the application is terminat- ing, and your data will never get saved.
Property list serialization is pretty cool and very easy to use, but it’s a little limiting, since only a small selection of objects can be stored in property lists. Let’s look at a little more robust approach.