Cai
Cai

Reputation: 3659

Accessing temp directory in Swift

I was trying to access temp directory in Swift. In Objective-C, I could use the following code to do so:

- (NSString *)tempDirectory {

    NSString *tempDirectoryTemplate =
    [NSTemporaryDirectory() stringByAppendingPathComponent:@"XXXXX"];
    const char *tempDirectoryTemplateCString = [tempDirectoryTemplate fileSystemRepresentation];
    char *tempDirectoryNameCString           = (char *)malloc(strlen(tempDirectoryTemplateCString) + 1);
    strcpy(tempDirectoryNameCString, tempDirectoryTemplateCString);
    char *result                             = mkdtemp(tempDirectoryNameCString);
    if (!result) {
        return nil;
    }
    NSString *tempDirectoryPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempDirectoryNameCString length:strlen(result)];
    free(tempDirectoryNameCString);
    return tempDirectoryPath;
}

However, I'm a bit confuse about the type conversion and casting from Objective-C to Swift, such as const char * or CMutablePointer<CChar>. Is there any documents that I should look into?

Thanks.

Upvotes: 16

Views: 25742

Answers (6)

Grimxn
Grimxn

Reputation: 22507

How about something like :

public extension FileManager {
    func createTempDirectory() throws -> String {
        let tempDirectory = (NSTemporaryDirectory() as NSString).appendingPathComponent(UUID().uuidString)
        try FileManager.default.createDirectory(atPath: tempDirectory,
                                                withIntermediateDirectories: true,
                                                attributes: nil)
        return tempDirectory
    }
}

It doesn't answer your question about char* but it's cleaner...

NSFileManager reference here.

Also check out this SO question regarding unique names.

Upvotes: 17

Antzi
Antzi

Reputation: 13444

According to Apple, use of NSTemporaryDirectory is discouraged:

See the FileManager method url(for:in:appropriateFor:create:) for the preferred means of finding the correct temporary directory. For more information about temporary files, see File System Programming Guide.

So instead, you should use FileManager.default.temporaryDirectory

or if you want an unique path:

let extractionPath = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)

Upvotes: 12

user1687195
user1687195

Reputation: 9196

Swift 3 and up

I think a good way to do this in swift is with an extension on FileManager. This should create a unique temporary folder and return the URL to you.

extension FileManager{

    func createTemporaryDirectory() throws -> URL {
        let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)

        try createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
        return url
    }
}

Upvotes: 5

Suragch
Suragch

Reputation: 512566

Swift 3 version

func createTempDirectory() -> String? {

    guard let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("myTempFile.xxx") else {
        return nil
    }

    do {
        try FileManager.default.createDirectory(at: tempDirURL, withIntermediateDirectories: true, attributes: nil)
    } catch {
        return nil
    }

    return tempDirURL.absoluteString
}

Upvotes: 4

Vladimir Kofman
Vladimir Kofman

Reputation: 1993

Swift 2.1 version:

func createTempDirectory() -> String? {

    let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("XXXXXX")

    do {
        try NSFileManager.defaultManager().createDirectoryAtURL(tempDirURL, withIntermediateDirectories: true, attributes: nil)
    } catch {
        return nil
    }

    return tempDirURL.absoluteString
}

Upvotes: 8

Martin R
Martin R

Reputation: 540045

A direct translation of your Objective-C code to Swift would be:

func tempDirectory()->String! {
    let tempDirectoryTemplate = NSTemporaryDirectory()  + "XXXXX"
    var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation().copy()
    let result : CString = reinterpretCast(mkdtemp(&tempDirectoryTemplateCString))
    if !result {
        return nil
    }
    let fm = NSFileManager.defaultManager()
    let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
    return tempDirectoryPath
}

It uses the same mkdtemp() BSD method as your original code. This method creates a directory name from the template which is guaranteed not to exist at the time where the method is called.

Thanks to Nate Cook who figured out that reinterpretCast() can be used to treat the UnsafePointer<CChar> returned by mkdtemp() as a CString, so that it can be passed to stringWithFileSystemRepresentation(), see Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString.


As of Xcode 6 beta 6, the reinterpretCast() is not necessary anymore and the above code can be simplified to

func tempDirectory()->String! {
    let tempDirectoryTemplate = NSTemporaryDirectory()  + "XXXXX"
    var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation()
    let result = mkdtemp(&tempDirectoryTemplateCString)
    if result == nil {
        return nil
    }
    let fm = NSFileManager.defaultManager()
    let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
    return tempDirectoryPath
}

Upvotes: 3

Related Questions