FileIO for iPhone

Moving along from simple controls such as AlertViews, Singletons, ScrollViews and ActionSheets, in order to write anything remotely useful, FileIO would have to be done.

FileIO in the iPhone OS would seem to take 3 main forms. (1) Flat file IO, (2) plist file format, and (3) plist Dictionary format. There is of course, SQLite, but i haven’t gotten there yet. Compare this to normal PERL or PHP development that i have done, which is greatly restricted to SQL and Flat file IO, this seems to be a fair bit of a choice. So the question is: Which is better for what?

After working on the code for a little while, i have come to a conclusion of the following.

  1. Flatfile IO would be the best for generic writing for blocks of information where change is limited. This however, should exclude things such as key value pairs. Extremely useful would be a situation whereby contents of the file has to be accessed as a string or as a block of strings. This is very similar to the traditional way a file is written or read from in PERL or PHP.
  2. plist file format is similar to Flat file IO, however, accessing or writing a file as an object of strings. This is allocated to an array. In PERL, this would be similar to assigning an array variable to a file handle. Thus, the use of this method depends on how you intend to interface with the objects in the file. Essentially, this makes no difference to the programmer as the strings of the objects would contain the information without the plist XML anyway.
  3. plist Dictionary format seems to differ from both the above in that it excels in storing key-value pairs. The dictionary object in Obj-C allows itself to be stored nicely in a plist file format which also makes extraction easy.

In all three cases, i prefer to use the documents directory as the main storage area for the app.

// Get Document Directory
- (NSString *) documentsDir {
	NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
	return [paths objectAtIndex:0];
}

The various snippets for the above three methods are provided here.

FileIO

// Flat file IO using strings. Entire  file will be read in as a sting and will have to split
// at new lines to obtain the entire file as an array. Alternatively, it would be easy
// to just append a new line to the file. Do note that the output of this format is a simple flat file

- (IBAction) seeFileFlat: (id) sender {
	NSFileManager *fm = [[NSFileManager alloc] init];
	if ([fm fileExistsAtPath:[[NSString alloc] initWithFormat:@"%@/data.txt", [self documentsDir]]]) {
		[self appendTextFile:@"data.txt" :input.text];
		fileContent.text = [self readTextFile:@"data.txt"];
	} else {
		NSLog(@"File didn't exist. Creating");
		[self writeTextFile:@"data.txt" :input.text];
		fileContent.text = [self readTextFile:@"data.txt"];
	}
}

- (void) writeTextFile:(NSString *) fileName: (NSString *) content {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSString *a = [[NSString alloc] initWithFormat:@"%@\n", content];
	[a writeToFile:filePath atomically:YES encoding:NSStringEncodingConversionAllowLossy error:nil];
}

- (void) appendTextFile:(NSString *) fileName: (NSString *) content {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSFileManager *fm = [[NSFileManager alloc] init];
	NSMutableString *a;
	if ([fm fileExistsAtPath:filePath]) {
		a = [[NSMutableString alloc] initWithContentsOfFile:filePath];
	} else {
		a = [[NSMutableString alloc] initWithString:@""];
	}
	[a appendFormat:@"%@\n", content];
	[a writeToFile:filePath atomically:YES encoding:NSStringEncodingConversionAllowLossy error:nil];
}

- (NSString *) readTextFile:(NSString *) fileName {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	return [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
}

plist file format

// Handling File IO by using arrays to read the entire file
// All new lines will already be read as an item in an array. makes it wasy to append a line into the
// file by adding in a new object. The output of this would be a flat file with plist format
- (IBAction) seeFileArray: (id) sender {
	NSFileManager *fm = [[NSFileManager alloc] init];
	if ([fm fileExistsAtPath:[[NSString alloc] initWithFormat:@"%@/array_data.txt", [self documentsDir]]]) {
		[self appendTextFile_Array:@"array_data.txt" :input.text];
		[self readTextFile_Array:@"array_data.txt"];
	} else {
		NSLog(@"File didn't exist. Creating");
		[self writeTextFile_Array:@"array_data.txt" :input.text];
		[self readTextFile_Array:@"array_data.txt"];
	}
}

- (void) readTextFile_Array:(NSString *) fileName {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSArray *data = [[NSArray alloc] initWithContentsOfFile:filePath];
	for (NSString *fdata in data) {
		NSLog(fdata);
	}
}

- (void) appendTextFile_Array:(NSString *) fileName: (NSString *) content {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSFileManager *fm = [[NSFileManager alloc] init];
	NSMutableArray *data;
	if ([fm fileExistsAtPath:filePath]) {
		data = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
	} else {
		data = [[NSMutableArray alloc] init];
	}
	[data addObject:content];
	[data writeToFile:filePath atomically:YES];
	[data release];
}

- (void) writeTextFile_Array:(NSString *) fileName: (NSString *) content {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSMutableArray *data = [[NSMutableArray alloc] init];
	[data addObject:content];
	[data writeToFile:filePath atomically:YES];
}

plist Dictionary Format

// Write a text file with Dictionary. This makes key value retrival easier.
// There isn't any append for this method as the file content is read
// and then reprinted from memory. This essentially creates a new file all the time.
- (IBAction) seeFileDict: (id) sender {
	NSFileManager *fm = [[NSFileManager alloc] init];
	if ([fm fileExistsAtPath:[[NSString alloc] initWithFormat:@"%@/dict_data.plist", [self documentsDir]]]) {
		[self writeDictPlistFile:@"dict_data.plist"];
		[self readDictPlistFile:@"dict_data.plist"];
	} else {
		NSLog(@"File didn't exist. Creating");
		[self writeDictPlistFile:@"dict_data.plist"];
		[self readDictPlistFile:@"dict_data.plist"];
	}
}

- (void) writeDictPlistFile:(NSString *) fileName {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSMutableDictionary *dict = [[NSMutableDictionary alloc]
									initWithDictionary:[[NSDictionary alloc] initWithContentsOfFile:filePath]
									copyItems:YES];
	NSArray *categories = [NSArray arrayWithObjects:@"Books", @"Planes",nil];
	for (NSString *item in categories) {
		NSMutableArray *values = [[NSMutableArray alloc]
								  initWithArray:[dict valueForKey:item]
								  copyItems:YES];
		[values addObject:[[NSString alloc] initWithFormat:@"Values of %@", item]];
		[dict setObject:values forKey:item];
		[values release];
	}
	[[[NSDictionary alloc] initWithDictionary:dict] writeToFile:filePath atomically:YES];
}

- (void) readDictPlistFile:(NSString *) fileName {
	NSString *filePath = [NSString stringWithFormat:@"%@/%@", [self documentsDir], fileName];
	NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
	NSArray *categories = [dict allKeys];
	for (NSString *item in categories) {
		NSLog(@"Cat: %@\n",item);
		NSArray *values = [dict valueForKey:item];
		for (NSString *data in values) {
			NSLog(@"D: %@\n",data);
		}
	}
}