How do I create a temporary file with Cocoa?
Asked Answered
D

11

46

Years ago when I was working with C# I could easily create a temporary file and get its name with this function:

Path.GetTempFileName();

This function would create a file with a unique name in the temporary directory and return the full path to that file.

In the Cocoa API's, the closest thing I can find is:

NSTemporaryDirectory

Am I missing something obvious or is there no built in way to do this?

Disingenuous answered 19/10, 2008 at 1:9 Comment(1)
Word of caution when using that C# API: it has a namespace of only 65k files and will throw an exception once that is exhausted. This has happened to us in production - not all programs diligently clean up their temp files.Biaxial
W
36

A safe way is to use mkstemp(3).

Warmth answered 19/10, 2008 at 1:37 Comment(2)
Not exactly "with Cocoa"... more like "with POSIX"Suntan
The documentation says the following: "The implementation of these functions calls arc4random(3), which is not reentrant. You must provide your own locking around this and other consumers of the arc4random(3) API." I don't see how I'm supposed to synchronize the usage of all arc4random consumers in my project and all the 3rd party libraries it's using.Ora
U
20

[Note: This applies to the iPhone SDK, not the Mac OS SDK]

From what I can tell, these functions aren't present in the SDK (the unistd.h file is drastically pared down when compared to the standard Mac OS X 10.5 file). I would use something along the lines of:

[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: @"%.0f.%@", [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"txt"]];

Not the prettiest, but functional

Unrepair answered 19/10, 2008 at 2:58 Comment(7)
This has the same race condition as mktemp(3): Separating creation of the filename from creation of the temporary file opens a window of vulnerability. Use mkstemp(3) as Graham Lee suggests.Converter
Sorry, I was in iPhone mode; mkstemp(3) as suggested by the original poster is fine, but it won't work on iPhone.Unrepair
On the iPhone, each app has its own subtree of the filesystem. NSTemporaryDirectory() returns something within the app bundle. You still race against yourself and against anything else that has write permission into your tmp dir, but I think that's only privileged processes.Sesame
@Sesame where did the asker say they were on iPhone?Warmth
Nowhere, other than in this answer that we're commenting on. :-) I was replying to the above. The lack of mkstemp on the phone is not as much of a problem on the phone as it would be on the mac.Sesame
This failes when called "at the same time" from more than one thread. I don't know how fine grained the method is (the documentation doesn't tell details about that) but for me it happened that my NSOperations were called too fast and the files got the same names. My solution was to add the NSOperations address to the filename since they are sure to differ: [NSString stringWithFormat: @"%d_%.0f.%@", self, [NSDate timeInterv...Firebrand
..or just use NSUUID, i.e. NSString *uuidString = [[NSUUID UUID] UUIDString];Philodendron
T
16

Apple has provided an excellent way for accessing temp directory and creating unique names for the temp files.

- (NSString *)pathForTemporaryFileWithPrefix:(NSString *)prefix
{
    NSString *  result;
    CFUUIDRef   uuid;
    CFStringRef uuidStr;

    uuid = CFUUIDCreate(NULL);
    assert(uuid != NULL);

    uuidStr = CFUUIDCreateString(NULL, uuid);
    assert(uuidStr != NULL);

    result = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@", prefix, uuidStr]];
    assert(result != nil);

    CFRelease(uuidStr);
    CFRelease(uuid);

    return result;
}

LINK :::: http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009245 see file :::AppDelegate.m

Tartaric answered 29/11, 2011 at 6:58 Comment(0)
E
10

I created a pure Cocoa solution by way of a category on NSFileManager that uses a combination of NSTemporary() and a globally unique ID.

Here the header file:

@interface NSFileManager (TemporaryDirectory)

-(NSString *) createTemporaryDirectory;

@end

And the implementation file:

@implementation NSFileManager (TemporaryDirectory)

-(NSString *) createTemporaryDirectory {
 // Create a unique directory in the system temporary directory
 NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
 NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:guid];
 if (![self createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:nil]) {
  return nil;
 }
 return path;
}

@end

This creates a temporary directory but could be easily adapted to use createFileAtPath:contents:attributes: instead of createDirectoryAtPath: to create a file instead.

Efflorescent answered 22/10, 2010 at 23:18 Comment(0)
B
10

If targeting iOS 6.0 or Mac OS X 10.8 or higher:

NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
Biaxial answered 15/7, 2014 at 10:2 Comment(0)
P
5

Swift 5 and Swift 4.2

import Foundation

func pathForTemporaryFile(with prefix: String) -> URL {
    let uuid = UUID().uuidString
    let pathComponent = "\(prefix)-\(uuid)"
    var tempPath = URL(fileURLWithPath: NSTemporaryDirectory())
    tempPath.appendPathComponent(pathComponent)
    return tempPath
}

let url = pathForTemporaryFile(with: "blah")
print(url)
// file:///var/folders/42/fg3l5j123z6668cgt81dhks80000gn/T/johndoe.KillerApp/blah-E1DCE512-AC4B-4EAB-8838-547C0502E264

Or alternatively Ssswift's oneliner:

let prefix = "blah"
let url2 = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(prefix)-\(UUID())")
print(url2)
Purl answered 3/7, 2015 at 11:12 Comment(2)
This code no longer works, but an equivalent version is even simpler: URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(prefix)-\(UUID())")Kidd
Thanks @Kidd updated the answer in a slightly more verbose manner.Purl
K
5

The modern way to do this is FileManager's url(for:in:appropriateFor:create:).

With this method, you can specify a SearchPathDirectory to say exactly what kind of temporary directory you want. For example, a .cachesDirectory will persist between runs (as possible) and be saved in the user's library, while a .itemReplacementDirectory will be on the same volume as the target file.

Kidd answered 16/6, 2017 at 18:17 Comment(0)
P
2

You could use mktemp to get a temp filename.

Predicative answered 19/10, 2008 at 1:24 Comment(1)
There's a race condition in mktemp(3), it's better to use mkstemp(3).Warmth
A
1

Don't use legacy APIs like NSTemporaryDirectory, get a proper URL instead from FileManager.

let tmpURL = FileManager
    .default
    .temporaryDirectory
    .appendingPathComponent(UUID().uuidString)

You'd still have to create the directory.

Alyshaalysia answered 29/4, 2020 at 10:34 Comment(0)
C
-1

You could use an NSTask to uuidgen to get a unique file name, then append that to a string from NSTemporaryDirectory(). This won't work on Cocoa Touch. It is a bit long-winded though.

Carvel answered 19/10, 2008 at 13:13 Comment(2)
Using NSTask to execute uuidgen is a bit overkill if all you want is a UUID.Arsphenamine
Note that 10.8+ has the NSUUID class to create UUIDs w/o leaving Cocoa land: developer.apple.com/library/ios/documentation/Foundation/…Philodendron
P
-1

Adding to @Philipp:

- (NSString *)createTemporaryFile:(NSData *)contents {
    // Create a unique file in the system temporary directory
    NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
    NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:guid];
    if(![self createFileAtPath:path contents:contents attributes:nil]) {
        return nil;
    }
    return path;
}
Postrider answered 7/7, 2014 at 3:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.