Created
October 25, 2018 09:17
-
-
Save steipete/d7a1506cdb1300cba0a3ae1b11450ab5 to your computer and use it in GitHub Desktop.
Based on https://nshipster.com/temporary-files/ I ran some experiments with the recommended replacement API for NSTemporaryDirectory(). It's not straightforward.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// tl;dr: FileManager.default.url(for:in:appropriateFor:create:) has unexpected behavior in Simulator and creates temporary folders outside the tmp/ directory. | |
let tmpDirClassic = NSTemporaryDirectory() | |
print("tmpDirClassic: \(tmpDirClassic)") | |
// This is the same code, just syntax sugar for NSTemporaryDirectory() | |
// https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/FileManager.swift#L1420-L1422 | |
let tmpDirClassicNewShim = FileManager.default.temporaryDirectory | |
print("tmpDirClassicNewShim: \(tmpDirClassicNewShim)") | |
// Simulator: /Users/steipete/Library/Developer/CoreSimulator/Devices/31C05637-B9C9-482C-A6CE-D063A7CECF64/data/Containers/Data/Application/A68D11A4-88BA-4FCF-A8B1-D102945D0740/tmp/ | |
// Device: /private/var/mobile/Containers/Data/Application/9D3C6BA4-EE6B-4573-8DC5-66A759D52BC0/tmp/ | |
// based on https://nshipster.com/temporary-files/ we should use following API, not NSTemporaryDirectory(): | |
do { | |
let temporaryDirectoryURL = try FileManager.default.url(for: .itemReplacementDirectory, | |
in: .userDomainMask, | |
appropriateFor: URL(fileURLWithPath: "/"), | |
create: true) | |
print("temporaryDirectoryURL: \(temporaryDirectoryURL)") | |
} catch { | |
// This prints: | |
// Simulator: Unexpected error: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “Macintosh HD” in the folder “Macintosh HD”." UserInfo={NSURL=file:///, NSUnderlyingError=0x6000008e0870 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}. | |
// Device: Unexpected error: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “System@snap-652843” in the folder “System@snap-652843”." UserInfo={NSURL=file:///, NSUnderlyingError=0x280af3a50 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}. | |
print("Unexpected error: \(error).") | |
} | |
// Using a more appropriate URL works better: | |
do { | |
// Simulator: /Users/steipete/Library/Developer/CoreSimulator/Devices/31C05637-B9C9-482C-A6CE-D063A7CECF64/data/Containers/Data/Application/CB992FF7-AAC5-4FEF-8839-33122960BB42/Library/Caches | |
// Device: /var/mobile/Containers/Data/Application/9D3C6BA4-EE6B-4573-8DC5-66A759D52BC0/Library/Caches/ | |
let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! | |
let temporaryDirectoryURL = try FileManager.default.url(for: .itemReplacementDirectory, | |
in: .userDomainMask, | |
appropriateFor: cacheURL, | |
create: true) | |
// Prints different paths depending on Simulator or device! | |
// Simulator: /Users/steipete/Library/Developer/CoreSimulator/Devices/31C05637-B9C9-482C-A6CE-D063A7CECF64/data/Containers/Data/Application/A68D11A4-88BA-4FCF-A8B1-D102945D0740/Library/(A Document Being Saved By URLForDirectoryTest 2) | |
// *** NOTICE HOW THIS IS OUTSIDE THE EXPECTED TEMP DIR! *** | |
// | |
// Device: /private/var/mobile/Containers/Data/Application/9D3C6BA4-EE6B-4573-8DC5-66A759D52BC0/tmp/(A Document Being Saved By URLForDirectoryTest) | |
print("temporaryDirectoryURL: \(temporaryDirectoryURL.path)") | |
// What happens if we change the appropriateFor URL to be just the App root? | |
// /Users/steipete/Library/Developer/CoreSimulator/Devices/31C05637-B9C9-482C-A6CE-D063A7CECF64/data/Containers/Data/Application/CB992FF7-AAC5-4FEF-8839-33122960BB42/ | |
// /var/mobile/Containers/Data/Application/9D3C6BA4-EE6B-4573-8DC5-66A759D52BC0/ | |
let appRoot = cacheURL.deletingLastPathComponent().deletingLastPathComponent() | |
let temporaryDirectoryURL2 = try FileManager.default.url(for: .itemReplacementDirectory, | |
in: .userDomainMask, | |
appropriateFor: appRoot, | |
create: true) | |
print("temporaryDirectoryURL2: \(temporaryDirectoryURL2.path)") | |
// Simulator: /Users/steipete/Library/Developer/CoreSimulator/Devices/31C05637-B9C9-482C-A6CE-D063A7CECF64/data/Containers/Data/Application/(A Document Being Saved By URLForDirectoryTest 3) | |
// *** NOTICE HOW THIS IS OUTSIDE THE EXPECTED TEMP DIR! *** | |
// Device: /private/var/mobile/Containers/Data/Application/9D3C6BA4-EE6B-4573-8DC5-66A759D52BC0/tmp/(A Document Being Saved By URLForDirectoryTest 2) | |
} catch { | |
print("Unexpected error: \(error).") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Run into a similar problem recently, here is Apple's response: https://developer.apple.com/forums/thread/735726?answerId=762035022#762035022 basically what they are saying is that
itemReplacementDirectory
should only be used, literally, for item replacement, which means copy the temp file to this directory, edit the file, then move it to overwrite or save as a new file.