Time for part two of my series on iPhone development basics (you can find part one on the EchoDitto Labs blog). Last time, I gave some tips on writing settings to a binary file using Apple's Foundation Library. This time I'll show you how to retrieve those settings -- either from a cached version of the property list or from the filesystem itself. As with the first article, let's dive in head first with some code.
First of all, you'll want to add a static NSDictionary member to your settings data controller class for storing the cached settings.
In your .h file:
NSDictionary *cachedSettings;
@property(nonatomic, retain) NSDictionary *cachedSettings;
In your .m file:
static NSDictionary *cachedSettings;
@synthesize cachedSettings;
Here's the loadSettings function. As you can see, I've implemented it as a class method -- there's really no reason to treat your settings data controller as anything other than a singleton with some class methods. Some argue against using singletons in Objective C. In most cases, I could be persuaded to agree; however, app-wide settings don't really make any sense implemented as anything except as global variables, and singletons are essentially the OOP equivalent of globals. I'd love if someone wanted to discuss this in a comment thread, however.
+ (NSDictionary *)loadSettings
{
if (cachedSettings == nil) {
NSArray *dirArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [NSString stringWithFormat:@"%@/settings.bin", [dirArray objectAtIndex:0]];
NSData *settings = [NSData dataWithContentsOfFile:path];
if (settings != nil) {
NSDictionary *dict = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:settings
mutabilityOption:0
format:nil
errorDescription:nil];
if (dict == nil)
NSLog(@"Error in loadSettings");
cachedSettings = [dict copy];
return dict;
}
return nil;
}
else {
return cachedSettings;
}
}
The outermost if block is the heart of our caching. Essentially, if cachedSettings contains data, loadSettings simply returns that data. Otherwise, it loads data out of the filesystem in a pattern much like the save function. Again, NSData does the file I/O for us. Cocoa is made of magic pixie dust. If the file read goes well, we unserialize the binary data stream into an NSDictionary and return that dictionary.
Next, let's revisit the save function I provided last time -- albeit with a minor modification to accommodate for caching.
+ (BOOL) saveSettings:(NSString *)username password:(NSString *)password
{
NSArray *dirArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [NSString stringWithFormat:@"%@/settings.bin", [dirArray objectAtIndex:0]];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:username, @"username", password, @"password", nil];
if (cachedSettings != nil) [cachedSettings release];
cachedSettings = [dict copy];
NSData *data = [NSPropertyListSerialization dataFromPropertyList:dict format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil];
if (data == nil) {
NSLog(@"Error in SettingsDataController saveSettings");
return NO;
}
NSLog(@"Writing to path: %@", path);
if ([data writeToFile:path options:NSAtomicWrite error:nil] == NO) {
NSLog(@"writeToFile error");
return NO;
}
NSLog(@"writeToFile success");
return YES;
}
Notice lines 4 and 5 of saveSettings:password:.
if (cachedSettings != nil) [cachedSettings release];
cachedSettings = [dict copy];
All we're doing here is checking if a cached version of the settings NSDictionary exists. If so, we release that memory and then copy in the new version that was saved to disk. By copying in a cached version during the save process, we prevent ever having to read from disk more than once. This is, of course, not much of a performance boost for anything we're doing with our Drupal/iPhone integration project, but it's still a best practice, and it's easy and quick enough to implement that there's no good excuse for omitting it.
You could presumably simply set the cachedSettings pointer to the NSDictionary object, rather than memcpying it over. That would probably align better with best practices. I'm still trying to nail down exactly how to retain and release variables in Objective C, however, and I went with the copy because I was more interested in building out new functionality than debugging BAD_ACCESS errors. Again, I'd love a little discussion in a comment thread below.

Legacy Comments