I've seen this question asked a dozen times on here and yet the solution do not seem to solve my situation.
I have some content that needs to be stored in the 'Application Support' directory, but I continue receiving errors when trying to write there.
func assetFilesDirectory(asset: Asset, shouldCreate: Bool) -> URL? {
do {
let applicationSupportFolderURL = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let folder = applicationSupportFolderURL.appendingPathComponent("\(asset)")
if !FileManager.default.fileExists(atPath: folder.path) {
if shouldCreate {
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)
} else {
return nil
}
}
return folder
} catch {
print(error)
return nil
}
}
When this code runs, I keep getting the following error:
"You don’t have permission to save the file “asset-backgrounds” in the folder “Application Support”."
I've tried toggling the value for the create parameter in the following line, but I get the same error with that too, just referencing the Application Support directory and Library instead.
let applicationSupportFolderURL = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
I haven't seen any reason why this should make any difference API-wise, but as information, I am running this code on the tvOS platform (physical device). There is no information indicating that Application Support should be permission-restricted on tvOS though.
I replaced your Asset (whatever that is) with a string and it worked fine:
func assetFilesDirectory(name: String, shouldCreate: Bool) -> URL? {
do {
let applicationSupportFolderURL = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let folder = applicationSupportFolderURL.appendingPathComponent(name)
if !FileManager.default.fileExists(atPath: folder.path) {
if shouldCreate {
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)
print("ok")
} else {
return nil
}
}
return folder
} catch {
print(error)
return nil
}
}
I called
let url = self.assetFilesDirectory(name: "test", shouldCreate: true)
print(url)
and I got back "ok" and the URL file:///blahblahblah/Library/Application%20Support/test which is just what one would expect.
Related
This question already has an answer here:
UIImage(contentsOfFile:) returning nil despite file existing in caches directory [duplicate]
(1 answer)
Closed 1 year ago.
Currently, if I want to create a directory hierarchy in Document directory, I would perform the following
Using NSSearchPathForDirectoriesInDomains (Works fine)
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let documentUrl1 = URL(string: documentsDirectory)!
//
// /Users/yccheok/Library/Developer/...
//
print("documentUrl1 -> \(documentUrl1)")
let dataPath = documentUrl1.appendingPathComponent("SubFolder1").appendingPathComponent("SubFolder2")
print("dataPath.absoluteString -> \(dataPath.absoluteString)")
if !FileManager.default.fileExists(atPath: dataPath.absoluteString) {
do {
try FileManager.default.createDirectory(atPath: dataPath.absoluteString, withIntermediateDirectories: true, attributes: nil)
print("Folder creation done!")
} catch {
print(error.localizedDescription)
}
}
But, if I use the following
Using FileManager.default.urls (Not working)
let documentUrl0 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
//
// file:///Users/yccheok/Library/Developer/...
//
print("documentUrl0 -> \(documentUrl0)")
let dataPath = documentUrl0.appendingPathComponent("SubFolder1").appendingPathComponent("SubFolder2")
print("dataPath.absoluteString -> \(dataPath.absoluteString)")
if !FileManager.default.fileExists(atPath: dataPath.absoluteString) {
do {
try FileManager.default.createDirectory(atPath: dataPath.absoluteString, withIntermediateDirectories: true, attributes: nil)
print("Folder creation done!")
} catch {
print(error.localizedDescription)
}
}
The following error will be printed
You can’t save the file “SubFolder2” because the volume is read only.
I was wondering, under what use case, that FileManager.default.urls will be useful? Thanks.
URL(string and absoluteString are the wrong APIs to work with file system paths.
Your code accidentally works because URL(string applied to a path creates a path rather than a valid URL, and absoluteString applied to a path returns the path because there is no scheme (file://).
However you are strongly discouraged from doing that. A file system URL must be created with URL(fileURLWithPath and you can get the path with the path property.
You are encouraged to use the FileManager API because it provides a better error handling and it provides also the URL related API which is preferred over the string path API.
This is the correct FileManager way to create the directory if it doesn't exist
let fm = FileManager.default
do {
let documentUrl = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
//
// file:///Users/yccheok/Library/Developer/...
//
print("documentUrl -> \(documentUrl)")
let dataURL = documentUrl.appendingPathComponent("SubFolder1").appendingPathComponent("SubFolder2")
print("dataPath.path -> \(dataURL.path)")
if !fm.fileExists(atPath: dataURL.path) {
try fm.createDirectory(at: dataURL, withIntermediateDirectories: true, attributes: nil)
print("Folder creation done!")
}
} catch { print(error) }
Trying to create a file using createFile API on iOS.
createFile(path, data, attr)
Document says,
Return Value:
true if the operation was successful or if the item already exists, otherwise false.
I wonder if the operation was not successful, how can I get the exact cause of the failure?. Expected some API like,
createFile(path, data, attr, &error)
Use Data.write(to:options:) instead, it throws.
I use FileManager.default.createDirectory to create folders. I create Data that I want to write, and then I use Data.write to save it to disk.
var docDir = URL()
do {
docDir = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
} catch {
fatalError("\(error)")
}
let subDir = docDir.appendPathComponents("mySubDir")
do {
try FileManager.default.createDirectory(
at: subdir,
withIntermediateDirectories: true,
attributes: nil
)
} catch {
if (error as NSError).code == 516 {
// "The file “x” couldn’t be saved in the folder “y” because a file with the same name already exists."
// This is OK.
} else {
fatalError("\(error)")
}
}
let filePath = subdir.appendingPathComponent("yourFileName")
let object:Codable = Whatever()
let data = try! JSONEncoder().encode(object)
do {
try data.write(to: filePath)
} catch {
fatalError("\(error)")
}
I do not know why Apple devs chose FileManager.createFile to be different. It seems strange to me.
I am developing a simple app for iOS.
And I was really surprised that when image is written like in the code below, the path stays valid before the app restarts. After the app restarts the new sandbox is created and this invalidates the previous path. I am trying to find a way to have sandbox independent file path. Is it even possible without going through the resolution cycle (i.e. FileManager.default.url...)?
static func saveToDocument(name: String, image: UIImage) -> String? {
guard let data = image.pngData() else { return nil; }
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
return nil
}
do {
let file = directory.appendingPathComponent("\(name).png");
try data.write(to: file!)
return file?.absoluteString
} catch {
return nil
}
}
Stop searching, there is no way, and there is a good reason for that: The safety of your data.
You always have to get the URL of the documents directory with FileManager.default.url(for, it's not that expensive.
Guarding the URL is not necessary, the documents folder is guaranteed to exist.
Return only the file name rather than the complete URL string and I recommend to make the function throw to hand over all errors.
enum FileError : Error {
case noData
}
static func saveToDocument(name: String, image: UIImage) throws -> String {
guard let data = image.pngData() else { throw FileError.noData }
let directory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = directory.appendingPathComponent("\(name).png")
try data.write(to: fileURL)
return fileURL.lastPathComponent
}
And never use NS... classes like NSURL in Swift if there is a native counterpart (URL).
Trying to create a nested directory within Application Support and it's failing. The error message I get is "You don’t have permission to save the file 'folder1' in the folder 'testApp'.
let path = getApplicationSupportDirectory()
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
let folder = path.appendingPathComponent("\(appName)/folder1", isDirectory: true)
print("[ERR]: Folder location: \(folder.relativePath)")
if !FileManager.default.fileExists(atPath: folder.relativePath) {
do {
try FileManager.default.createDirectory(atPath: folder.relativeString, withIntermediateDirectories: true, attributes: nil)
} catch {
print("[ERR]: \(error.localizedDescription)")
}
}
Folder location outputs the correct location and it appears to create the first directory within "appName".
Please try this code, it's supposed to work.
It uses the API of FileManager to create the Application Support folder if it does not exist.
do {
let applicationSupportFolderURL = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
let folder = applicationSupportFolderURL.appendingPathComponent("\(appName)/folder1", isDirectory: true)
print("[ERR]: Folder location: \(folder.path)")
if !FileManager.default.fileExists(atPath: folder.path) {
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)
}
} catch { print(error) }
I am a new student in 9th grade learning swift, creating a school project .
I am trying to create a directory where I want to save a scanned file into pdf format.
While creating directory I am getting error below.
Error 1:
Cannot use instance member 'filemgr' within property initializer; property initializers run before 'self' is available.
Error 2:
Expected declaration
Code:
let filemgr = FileManager.default
let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)
let docsURL = dirPaths[0]
let newDir = docsURL.appendingPathComponent("data").path
do{
try filemgr.createDirectory(atPath: newDir,withIntermediateDirectories: true, attributes: nil)
} catch {
print("Error: \(error.localizedDescription)")
}
Please assist me in resolving this issue.
Thanks.
Please use this code:
Swift 5.0,
Swift 4.0 And
Swift 3.0
let DocumentDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let DirPath = DocumentDirectory.appendingPathComponent("FOLDER_NAME")
do
{
try FileManager.default.createDirectory(atPath: DirPath!.path, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
print("Unable to create directory \(error.debugDescription)")
}
print("Dir Path = \(DirPath!)")
For Swift 4.0
Please use this
let fileManager = FileManager.default
if let tDocumentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let filePath = tDocumentDirectory.appendingPathComponent("\(FOLDER_NAME)")
if !fileManager.fileExists(atPath: filePath.path) {
do {
try fileManager.createDirectory(atPath: filePath.path, withIntermediateDirectories: true, attributes: nil)
} catch {
NSLog("Couldn't create document directory")
}
}
NSLog("Document directory is \(filePath)")
}
For Swift 4.0, I created the following extension off of URL that allows for the creation of a folder off of the documents directory within the application.
import Foundation
extension URL {
static func createFolder(folderName: String) -> URL? {
let fileManager = FileManager.default
// Get document directory for device, this should succeed
if let documentDirectory = fileManager.urls(for: .documentDirectory,
in: .userDomainMask).first {
// Construct a URL with desired folder name
let folderURL = documentDirectory.appendingPathComponent(folderName)
// If folder URL does not exist, create it
if !fileManager.fileExists(atPath: folderURL.path) {
do {
// Attempt to create folder
try fileManager.createDirectory(atPath: folderURL.path,
withIntermediateDirectories: true,
attributes: nil)
} catch {
// Creation failed. Print error & return nil
print(error.localizedDescription)
return nil
}
}
// Folder either exists, or was created. Return URL
return folderURL
}
// Will only be called if document directory not found
return nil
}
}
If the desired folder does not exist, it will create it. Then, assuming the folder exists, it returns the URL back to the user. Otherwise, if it fails, then nil is returned.
For example, to create the folder "MyStuff", you would call it like this:
let myStuffURL = URL.createFolder(folderName: "MyStuff")
This would return:
file:///var/mobile/Containers/Data/Application/4DE0A1C0-8629-47C9-87D7-C2B4F3A16D24/Documents/MyStuff/
You can also create nested folders with the following:
let myStuffHereURL = URL.createFolder(folderName: "My/Stuff/Here")
Which gives you:
file:///var/mobile/Containers/Data/Application/4DE0A1C0-8629-47C9-87D7-C2B4F3A16D24/Documents/My/Stuff/Here/
You are getting this because you are assigning value to newDir instance at wrong place.
I wrote your code in viewDidLoad and it works perfectly.
For Swift 5 and up Version
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryURL = documentDirectoryURL.appendingPathComponent("FolderName", isDirectory: true)
if FileManager.default.fileExists(atPath: directoryURL.path) {
print(directoryURL.path)
} else {
do {
try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
print(directoryURL.path)
} catch {
print(error.localizedDescription)
}