75

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.

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150

2 Answers2

189

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.

cocoafan
  • 4,884
  • 4
  • 37
  • 45
Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • 6
    Don't build the path to Caches yourself. Use the same `NSSearchPathForDirectoriesInDomain()` function, but instead of using `NSDocumentDirectory`, use `NSCachesDirectory`. – Dave DeLong Oct 14 '09 at 15:43
  • Edited answer to use `NSCachesDirectory` – Dave DeLong Oct 14 '09 at 15:46
  • http://idevkit.com/iphonedev/2009/09/saving-nsmutablearrays/ there is a section in there on how to get the documents directory of your app, xcodeproj is included. It is based on saving an nsmutablearray and loading it again. It is essentially the same method for gettig the directory for saving other objects to. – Sj. Oct 14 '09 at 16:56
  • 2
    That's nice but is tangental to the question, which is simply how to get an area you can write to. Plus the idea of StackOverflow is that this should act as the canonical repository of information. – Kendall Helmstetter Gelner Oct 14 '09 at 17:34
  • According to: [iOS Application Programming Guide ](http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StandardBehaviors/StandardBehaviors.html%23//apple_ref/doc/uid/TP40007072-CH4-SW6), NSLibraryDirectory is not a recommended path, instead Apple suggests the NSApplicationSupportDirectory. Maybe the wiki should be updated to reflect this? – hwaxxer Feb 15 '11 at 10:12
  • I don't know if I read it that way, they offer NSApplicationSupportDirectory as a "common" directory but don't have anything to say (that I could find) against using Library directly... and since NSApplicationSupportDirectory returns a directory inside the application Library directory, it seems like a minor difference. I would say which one to use would depend on if you had to actually create the "Application Support" directory like you do with Caches. – Kendall Helmstetter Gelner Feb 15 '11 at 16:36
  • @KendallHelmstetterGelner I upvoted both the question and the reply. Thank you for sharing this as community wiki. I asked a question in http://stackoverflow.com/questions/12378228/clarification-needed-about-where-i-should-put-store-my-core-datas-sqllite-fil. I would like to know, if possible, if these patterns continue to be valid based on my question. Thanks. – Lorenzo B Sep 12 '12 at 11:33
5

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];
Pierre Houston
  • 1,631
  • 20
  • 33