Thursday, October 15, 2009

How to locate, read and write property lists for iPhone

I had a real headache trying to figure out how to read and write property lists yesterday. It should have been an easy task, and I did everything according to the manual.

Here is what I wanted to do:
  1. Locate a property list created in xcode
  2. Read the XCode property list to a NSDictionary
  3. Append one or more extra items to the NSDictionary
  4. Save the updated NSDictionary to disc
The problem was that the iPhone has a sandboxed area called "Documents" where you can read and write property lists, but the property list you created in xcode is not located in this folder.

If you create a property list "MyPlist.plist" for an application called "MyApp" in XCode it will be located here:

appPath/MyApp.app/MyPlist.plist

Where "appPath" is a string that you can only get while running the app. You can get the whole path to the plist by using this code:

NSString *path = [[NSBundle mainBundle] pathForResource:@"MyPlist" ofType:@"plist"];

This file could be read from, but not written to due to the location on the iPhone.

But I wanted to save data to the iPhone. This could only be done in a property list located here:

appPath/Documents/MyPlist.plist

To locate the folder "Documents" where I have rights to both read and write I can get the path by using this code:

NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
NSString *path=[documentsDirectory stringByAppendingPathComponent:@"History.plist"];

Reading and writing to the property list from a NSDictionary could be done with the following code:

Reading: NSDictionary *dict=[[NSDictionary alloc] initWithContentsOfFile:path];
Writing: [dict writeToFile:path atomically:YES];

Here is the whole code put into action:

NSMutableDictionary *dict;

//Reading plist created in xcode
NSString *path = [[NSBundle mainBundle] pathForResource:@"MyPlist" ofType:@"plist"];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) dict = [[NSMutableDictionary alloc] initWithContentsOfFile:path];

//Reading plist stored on device
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
path=[documentsDirectory stringByAppendingPathComponent:@"MyPlist.plist"];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
//Merging dictionary created in xcode with dictionary stored on the iPhone
[dict addEntriesFromDictionary:tempDict];
}

//Creating dictionary if it doesn't exist
if (dict==nil) dict=[[NSMutableDictionary alloc] init];

//creating and adding array to dictionary
NSArray *array=[NSArray arrayWithObjects: [NSDate date], [NSNumber numberWithDouble:double1], [NSNumber numberWithDouble:double2], [NSNumber numberWithDouble:double3], [NSNumber numberWithDouble:double4],nil];
[dict setObject:array forKey:@"First Array"];

//writing dictionary to plist on device
[dict writeToFile:path atomically:YES];
[dict release];