How can I get a writable path on the iPhone?

IphoneFilesystems

Iphone Problem Overview


I am posting this question because I had a complete answer for this written out for another post, when I found it did not apply to the original but I thought was too useful to waste. Thus I have also made this a community wiki, so that others may flesh out question and answer(s). If you find the answer useful, please vote up the question - being a community wiki I should not get points for this voting but it will help others find it

How can I get a path into which file writes are allowed on the iPhone? You can (misleadingly) write anywhere you like on the Simulator, but on the iPhone you are only allowed to write into specific locations.

Iphone Solutions


Solution 1 - Iphone

There are three kinds of writable paths to consider - the first is Documents, where you store things you want to keep and make available to the user through iTunes (as of 3.2):

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

Secondly, and very similar to the Documents directory, there is the Library folder, where you store configuration files and writable databases that you also want to keep around, but you don't want the user to be able to mess with through iTunes:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];

Note that even though the user cannot see files in iTunes using a device older than 3.2 (the iPad), the NSLibraryDirectory constant has been available since iPhoneOS 2.0, and so can be used for builds targeting 3.0 (or even earlier if you are still doing that). Also the user will not be able to see anything unless you flag an app as allowing users to modify documents, so if you are using Documents today you are fine as long as you change location when updating for support of user documents.

Last there is a cache directory, where you can put images that you don't care exist for the long term or not (the phone may delete them at some point):

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO) {
	[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}

Note that you have to actually create the Caches directory there, so when writing you have to check and create every time! Kind of a pain, but that's how it is.

Then when you have a writable path, you just append a file name onto it like so:

NSString *filePath =  [documentsDirectory stringByAppendingPathComponent:@"SomeDirectory/SomeFile.txt"];

or

NSString *filePath =  [cachePath stringByAppendingPathComponent:@"SomeTmpFile.png"];

Use that path for reading or writing.

Note that you can make subdirectories in either of those writable paths, which one of the example string above is using (assuming one has been created).

If you are trying to write an image into the photo library, you cannot use file system calls to do this - instead, you have to have a UIImage in memory, and use the UIImageWriteToSavedPhotosAlbum() function call defined by UIKit. You have no control over the destination format or compression levels, and cannot attach any EXIF in this way.

Solution 2 - Iphone

Thanks to Kendall & Dave, above, and I thought this amendment was useful to bring up. When using for one-off debug code, I used this trick from Mike Ash's NSBlog to eliminate the temporary variables isDir & error, minimizing the number of lines and making the verbosity almost bearable:

NSFileHandle *dumpFileHandle = nil;
#ifdef DEBUG
    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&(BOOL){0}])
        [[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:YES attributes:nil error:&(NSError*){nil}];
    NSString *dumpPath = [cachePath stringByAppendingPathComponent:@"dump.txt"];
    [[NSFileManager defaultManager] createFileAtPath:dumpPath contents:nil attributes:nil];
    [(dumpFileHandle = [NSFileHandle fileHandleForWritingAtPath:dumpPath]) truncateFileAtOffset:0];
#endif

if (dumpFileHandle) [dumpFileHandle writeData:blah];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKendall Helmstetter GelnerView Question on Stackoverflow
Solution 1 - IphoneKendall Helmstetter GelnerView Answer on Stackoverflow
Solution 2 - IphonePierre HoustonView Answer on Stackoverflow