Volumes have been written about persisting data, but when it comes to short-lived, temporary files, there is very little to go on for Objective-C. (Or if there has, perhaps it was poetically ephemeral itself).
Temporary files are used to write a buffer to disk, to either be atomically moved to a permanent location, or processed in some manner and then discarded. Creating a temporary file involves finding the appropriate part of the filesystem, generating a unique name, and moving or deleting the file after you’re finished using it.
Finding an Enclosing Directory
The first step to creating temporary files or directories is to find a reasonable, out-of-the-way place to write to—somewhere that won’t be backed up by Time Machine or synced to iCloud or the like.
On Unix systems, the
/tmp directory was the de facto scratch space, but with the sandboxed containers of iOS and OS X apps today, a hard-coded path just won’t cut it.
NSTemporaryDirectory is a Foundation function that returns the directory designated for writing short-lived files on the targeted platform.
A Wild Goose Chase
In recent years, Apple has pushed to extricate filesystem path operations from
NSString APIs, recommending that users switch to using
NSURL-based APIs for classes like
NSFileManager. Unfortunately, the migration has not been entirely smooth.
Consider the documentation for
URLForDirectory:inDomain:appropriateForURL:create:error:for the preferred means of finding the correct temporary directory.
Alright, fair enough. Let’s see what’s going on with
You can also use this method to create a new temporary directory for storing things like autosave files; to do so, specify
NSItemReplacementDirectoryfor the directory parameter,
domainparameter, and a valid parent directory for the
urlparameter. After locating (or creating) the desired directory, this method returns the URL for that directory.
Actually, this method appears to be intended for moving existing temporary files to a permanent location on disk with
-replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:. Not exactly what we’re looking for.
So much for the
NSString filesystem API migration. Let’s stick to something that works:
[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
Generating a Unique Directory or File Name
With a place to call home (temporarily), the next step is to figure out what to name our temporary file. We don’t really care what temporary files are named—the only real concern is that they’re unique, so as to not interfere with, or be interfered by, any other temporary files.
The best way to generate a unique identifier is to use the
globallyUniqueString method on
NSString *identifier = [[NSProcessInfo processInfo] globallyUniqueString];
This will return a string in the format:
Other sources advise the direct invocation of the
mktemp(3)system command in order to mitigate potential conflicts. However, using
NSProcessInfo -globallyUniqueStringto generate unique names is extremely unlikely to result in a collision.
NSUUID (discussed previously) also produces workable results, assuming that you’re not doing anything too crazy.
[[NSUUID UUID] UUIDString]
This produces a string in the format:
Creating a Temporary File Path
Using the aforementioned technique for generating unique identifiers, we can create unique temporary file paths:
NSString *fileName = [NSString stringWithFormat:@"%@_%@", [[NSProcessInfo processInfo] globallyUniqueString], @"file.txt"]; NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
Creating a Temporary Directory
In situations where many temporary files might be created by a process, it may be a good idea to create a temporary sub-directory, which could then be removed for easy cleanup.
Creating a temporary directory is no different than any other invocation of
NSURL *directoryURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] isDirectory:YES]; [[NSFileManager defaultManager] createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil error:&error];
And, of course, temporary file paths relative to this directory can be created with
NSURL *fileURL = [directoryURL URLByAppendingPathComponent:fileName];
Writing to a Temporary File
Files don’t exist on the file system until a particular file path is either touched or written to.
There are several ways in which data is written to disk in Foundation. The most straightforward of which is
NSData *data = ...; NSError *error = nil; [data writeToURL:fileURL options:NSDataWritingAtomic error:&error];
For more advanced APIs, it is not uncommon to pass an
NSOutputStream instance to direct the flow of data. Again, creating an output stream to a temporary file path is no different than any other kind of file path:
NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:[fileURL absoluteString] append:NO];
The final step is what makes a temporary file actually temporary: clean up.
Although files in a system-designated temporary directory make no guarantees about how long they’ll exist before being deleted automatically by the operating system (up to a few days, according to scattered reports), it’s still good practice to take care of it yourself once you’re finished.
Do that with
NSFileManager -removeItemAtURL:, which works for both a temporary file and a temporary directory:
NSError *error = nil; [[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
“This too shall pass” is a mantra that acknowledges that all things are indeed temporary. Within the context of the application lifecycle, some things are more temporary than others, and it is in that knowledge that we act appropriately, seeking to find the right place, make a unique impact, and leave without a trace.
Perhaps we can learn something from this cycle in our own, brief and glorious lifecycle.