2012-09-05

Some notes about file locations in OS X:

Application Resources

Use [[NSBundle mainBundle] URLForResource:...] or [[NSBundle mainBundle] pathForResource:...] to retrieve files distributed with the application.

Preferences

The User Domain values accessed by NSUserDefaults are serialized to a file ~/Library/Preferences/application.plist. If you are rebuilding a program and want to test your preference handling with a blank slate, you can /bin/rm that file.

The ~/Library/Preferences/application.LSSharedFileList.plist contains the list of files that appear in the "Open Recent" menu.

Temporary Files

---- update 2012-10-26 ---- On OS X 10.6.8, there are mysteriously-named directories under /var/folders/ for each user. The temporary files directory is buried two layers below that. A temporary directory might be something like /var/folders/Ky/KyeZSNnoFuMkG17Ne8wUtF+++TK/-Tmp-/.

To get a string holding the temporary directory for the current user, use NSTemporaryDirectory(). To create a working directory under that directory, try this code:

	NSString *workingDirectory = nil;
	char *template = strdup([[NSTemporaryDirectory() stringByAppendingPathComponent: @"myPrefixCharsXXXXXXXX"] UTF8String]);
	if (mkdtemp(template)) {
		tempDir = [[NSString stringWithUTF8String: template] retain];
	} else {
		// Error
	}

Apple's Mac App Programming Guide warns you that files in the temporary directory may be purged at system startup. Apparently this includes a sleep-resume cycle. When resuming from sleep mode under OS X 10.6.8, I believe the system is deleting files from the temporary directory that are not referenced by an open file handle. This is a problem for an application that needs to extract the contents of an archive to a temporary location and then access the constituent files at random. See below for a different implementation that uses the Caches directory instead.

Home Directories

Use NSHomeDirectory() for the current user or NSHomeDirectoryForUser() for any other user. For a non-sandboxed app, the home directory is /Users/userid. For a sandboxed app, the home directory will be a container directory somewhere below that directory.

Miscellaneous

Most other directories can be located using NSSearchPathForDirectoriesInDomains(directory,domainMask,YES).

The domain mask values can be OR'ed together.

Only one directory type can be specified. The full list can be found in the Foundation Data Types reference. These are the ones I found interesting through OS X 10.5:

~/Library/Application Support is the preferred location for storing persistent files specific to a user. /Library/Application Support used to be the designated location for storing persistent files common to multiple users, but that concept is now deprecated. (The App Store Guidelines and the App Sandbox Guide discourage any type of inter-user contact.) Nevertheless, you could follow the final suggestion from this Stackoverflow question and create a file there anyway.

I did not understand Apple's docs regarding the ~/Library/Caches contents at first. If the files will not be purged (except by the app), why would it be important that they can be regenerated? Matt Gallagher claims that the distinction is rooted in the behavior of Time Machine. Files in ~/Library/Caches are not backed up but the contents of ~/Library/Application Support are saved.

This is my alternate code for creating a temporary directory in the ~/Library/Caches directory so that its contents will survive system restarts. If ~/Library/Caches does not exist, it falls back to NSTemporaryDirectory().

	const NSString *applicationID = "com.mycompany.myapp";
	const NSString *tempDirTemplate = "myPrefixCharsXXXXXXXX";
	NSString *tempDir = nil;

	NSArray *cdirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
	NSString *cdpath = [cdirs count] ? [cdirs objectAtIndex: 0] : NSTemporaryDirectory();
	cdpath = [cdpath stringByAppendingPathComponent: applicationID];
	BOOL ok = [[NSFileManager defaultManager] createDirectoryAtPath: cdpath
			withIntermediateDirectories: YES
			attributes: [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0755] forKey: NSFilePosixPermissions]
			error: NULL];
	if (ok) {
		char *template = strdup([[cdpath stringByAppendingPathComponent: tempDirTemplate] UTF8String]);
		if (mkdtemp(template)) {
			tempDir = [[NSString stringWithUTF8String: template] retain];
		}
		free(template);
	}
	if (nil != tempDir) {
		// Do work
	} else {
		// Complain
	}