I have an app for iOS that uses metal to render obj files. I am trying to add functionality for users to insert the url of an obj file online and render that. I am using alamofire and am not sure how I will access the file once downloaded, since I won't know the file name.
let destination = DownloadRequest.suggestedDownloadDestination(for: .downloadsDirectory)
let modelUrl = URL(string: "https://drive.google.com/file/d/110KRnku3N_K_EIN-ZLYXK128zjMqxGLM/view?usp=sharing")
Alamofire.download(
modelUrl!,
method: .get,
parameters: Parameters.init(),
encoding: JSONEncoding.default,
headers: nil,
to: destination).downloadProgress(closure: { (progress) in
//progress closure
}).response(completionHandler: { (DefaultDownloadResponse) in
//here you able to access the DefaultDownloadResponse
//result closure
})
let file = try? String(contentsOf: URL(string: (NSSearchPathForDirectoriesInDomains(.downloadsDirectory, .userDomainMask, true)[0]))!)
I am also fairly certain my method for retrieving the file will not work, but i'm not sure how to search the documents directory for a specific file.
The files I have working are in the project as .obj files in xcode, and I simply use this.
let assetURL = Bundle.main.url(forResource: modelName, withExtension: "obj")
Bundle.main will not return files in document directory, it is use for the files you put in your main bundle (inside Xcode while development usually). You need to use FileManager to access files in document directory. You can use this function to search files in your document directory.
func getFilePathInDocuments(fileName:String) -> String {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: path)
let fileManager = FileManager.default
let filePath = url.appendingPathComponent(fileName).path
if (fileManager.fileExists(atPath: filePath)) {
return filePath
}else{
return ""
}
}
This is how you call it:
let foundPath = getFilePathInDocuments(fileName: "fileName.obj")
Update:
You can give a fileName to Almofire and you will receive the downloaded URL too from it.
let destinationPath: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0];
let fileURL = documentsURL.appendingPathComponent("fileName")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(url, to: destinationPath)
.downloadProgress { progress in
}
.responseData { response in
}
To get downloaded document directory URL use response.destinationURL.
Related
I'm working on an iOS application. In the app, I am using Alamofire to create a POST request that returns a raw PDF file in response. Right now, I am able to save the file and open it with UIDocumentInteractionController. But, I want the file to stay in User's documents folder.
Here's how I create the destination path:
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("Dividend Summary Report.pdf")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Someone please tell me what's wrong with my logic and what I can do to correct it.
Well you need to check the file status if it exists then read from documentDirectory else download the file.
Create function like this:
func checkIfFileExists(urlString: String) {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let fileName = urlString
let fileManager = FileManager.default
let filePath = url.appendingPathComponent("\(fileName).pdf")?.path
print("filePath : \(String(describing: filePath))")
if fileManager.fileExists(atPath: filePath!) {
print("File exists")
} else {
print("File doesn't exists")
// set your download function here
}
}
This portion of the code is supposed to download a txt file from a website. By running this code it is able to successfully download the file from the website and place it into the app's Documents directory. I am able to see where the file is stored because in the last couple of lines in the code it prints out the location of the file. However, I am not able to get the file name that was recently downloaded. The goal is for me to try to get the name of the file so that I can be able to open it and read from it. What are my options in approaching this? What am I missing in this block of code that is preventing me from getting the name of the file that was recently downloaded?
guard let url1 = URL(string: website) else { return }
//This portion of the code focuses on creating a download task with a completion handler
//Completion handler moves the downloaded file to the app's directory
let downloadTask = URLSession.shared.downloadTask(with: url1) {
urlOrNil, responseOrNil, errorOrNil in
// check for and handle errors:
// * errorOrNil should be nil
// * responseOrNil should be an HTTPURLResponse with statusCode in 200..<299
guard let fileURL = urlOrNil else { return }
do {
let documentsURL = try
FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let savedURL = documentsURL.appendingPathComponent(
fileURL.lastPathComponent)
try FileManager.default.moveItem(at: fileURL, to: savedURL)
} catch {
print ("file error: \(error)")
}
}
downloadTask.resume()
//If you want to receive progress updates as the download proceeds, you must use a delegate.
var urlSession = URLSession(configuration: .default, delegate: self as? URLSessionDelegate, delegateQueue: nil)
func startDownload(url1: URL) -> String? {
let downloadTask = urlSession.downloadTask(with: url1)
let fname = downloadTask.response?.suggestedFilename
downloadTask.resume()
return fname
//self.downloadTask = downloadTask
}
let name = startDownload(url1: url1)
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let documentsDir = paths.firstObject as! String
print("Path to the Documents directory\n\(documentsDir)")
You can get the name of the file at a given path by initialising a URL object from the path:
let name = URL(fileURLWithPath: yourPath).lastPathComponent
This returns an optional string. The name will be automatically unescaped, so it will be human-readable (no percent encoding).
The filename should be available to you from your fileURL variable, as this is where you got the filename in order to save it:
let name = fileURL.lastPathComponent
My Scenario, I am trying to delete all files from particular document directory by using document folder path. Here, every time I am saving file within my application document directory folder, by using below code I can’t able to delete files
let urlString: String = myurl.absoluteString
print("FILEURL:\(urlString)")
do {
try fm.removeItem(atPath: "\(myurl)")
} catch let error as NSError {
print(error.debugDescription)
}
You are mixing up URL and String path
Either use the String related API
try fm.removeItem(atPath: myurl.path) // NEVER use .absoluteString for a file system path
or use the URL related API (recommended)
try fm.removeItem(at: myurl)
To remove all files get the file URLs in the enclosing directory with contentsOfDirectory(at:includingPropertiesForKeys:options:) and remove one by one
let fileManager = FileManager.default
do {
let documentDirectoryURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURLs = try fileManager.contentsOfDirectory(at: documentDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
for url in fileURLs {
try fileManager.removeItem(at: url)
}
} catch {
print(error)
}
For delete specific file or folder using Url
let filePathString = "file:///Users/mac-01/Library/Developer/CoreSimulator/Devices/092D2386-5B43-4D98-8DCF-F21E08CCD400/data/Containers/Data/Application/C6D910A2-67D9-48A4-8221-5C81C722D508/Documents/Products"
guard let fileUrl = URL(string: "\(filePathString)") else { return }
do {
try FileManager.default.removeItem(at: fileUrl)
print("Remove successfully")
}
catch let error as NSError {
print("An error took place: \(error)")
}
removeItem method is accept url of document directory file or folder.Please try with it.
For Delete Document directory folder use
let fileManager = FileManager.default
let myDocuments = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
try fileManager.removeItem(at: myDocuments)
} catch {
return
}
Below is my code -
I have tried to get the document directory path and with standard FileManager singleton tried to create a file, but I am not able to create the file, as the error -
Unable to store data: Error Domain=NSCocoaErrorDomain Code=4 "The file “CrashLog.txt” doesn’t exist."
UserInfo={NSFilePath=file:///Users/ABC/Library/Developer/CoreSimulator/Devices/87317777-63E7-422B-A55F-878E3267AFB8/data/Containers/Data/Application/4B41AA87-E4B9-4EE4-A67F-AC3B018913CC/Documents/CrashLog,
NSUnderlyingError=0x600000244ec0 {Error Domain=NSPOSIXErrorDomain
Code=2 "No such file or directory"}}
Code in development -
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if (paths.count > 0) {
let documentsDirectory = paths[0]
let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt").absoluteString
let _string = "Hello"
//Create file at given path
let data = _string.data(using: .utf8)
//let attributes = FileManager.default.attributesOfItem(atPath: logFilePath)
let fileExists : Bool = FileManager.default.fileExists(atPath: logFilePath)
print(fileExists)
let isFileCreated = FileManager.default.createFile(atPath: logFilePath, contents: data, attributes: nil)
print("ifFileCreated", isFileCreated)
}
Here's my take on what you've done. Adopt the URL-based means of working with files. The best way to write data (for this example, at least), is to use Data's ability (not FileManager) to write to a file, again, using a URL. In most cases, you don't need to worry whether the file exists or not; just do it, and handle any error that arises.
if var url = try? FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false) {
url = url.appendingPathComponent("CrashLog").appendingPathExtension("txt")
let _string = "Hello"
if let data = _string.data(using: .utf8) {
do {
try data.write(to: url)
print("successful")
} catch {
print("unsuccessful")
}
}
}
The appendingPathComponent method if the receiver (e.g. parameter) does not end with a trailing slash, then it may read file metadata to determine whether the resulting path is a directory. That means it may produce the error you are seeing, so better use the appendingPathComponent(_:isDirectory:) instead.
For example:
let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt", isDirectory: false).absoluteString
The API absoluteString is wrong. The correct API is path
absoluteString returns the entire URL string representation including the scheme file://. On the other hand the path API of FileManager expects file system paths, the string without the scheme.
You are encouraged to use the URL related API anyway and you can write Data directly to disk without explicitly creating a file.
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let logFileURL = documentsURL.appendingPathComponent("CrashLog.txt")
let string = "Hello"
let data = Data(string.utf8)
let fileExists = FileManager.default.fileExists(atPath: logFileURL.path)
print(fileExists)
do {
try data.write(to: logFileURL)
print("data written")
} catch { print(error) }
I am attempting to upload an image file(.jpg) and a pdf file(.pdf) at the ssme time using Alamofire. I keep getting the following error which indicates that the files that I am trying to upload does not exist, which in fact they do.
multipartEncodingFailed(Alamofire.AFError.MultipartEncodingFailureReason.bodyPartFileNotReachableWithError(atURL: file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg, error: Error Domain=NSCocoaErrorDomain Code=260 "The file “tempImage_wb.jpg” couldn’t be opened because there is no such file." UserInfo={NSURL=file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg, NSFilePath=/var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg, NSUnderlyingError=0x1c4841020 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}))
My code for uploading the files:
// GET URL FOR IMAGE AND PDF FILES
guard
let imageURLString = UserDefaults.standard.string(forKey: "URL_IMAGE"),
let pdfURLString = UserDefaults.standard.string(forKey: "URL_PDF") else{return}
guard
let imgURL = URL.init(string: imageURLString),
let pdfURL = URL.init(string: pdfURLString) else{return}
var arrayURLToUpload: [URL] = []
arrayURLToUpload.append(imgURL)
arrayURLToUpload.append(pdfURL)
let sendParamters = ["user_id": "1", "hashTagArray": jsonArrayHashTags]
Alamofire.upload(
multipartFormData: { multipartFormData in
for(key, value) in sendParamters{
multipartFormData.append((value.data(using: .utf8)!), withName: key)
}
for fileURL in arrayURLToUpload{
print("fileURL: \(fileURL)")
multipartFormData.append(fileURL, withName: "file[]")
}
},
to: UPLOAD_URL,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
}
/**TRACK PROGRESS OF UPLOAD**/
upload.uploadProgress { progress in
print(progress.fractionCompleted)
}
/***/
case .failure(let encodingError):
print(encodingError)
}
}
)
console:
fileURL: file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg
fileURL: file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImagePDF.pdf
I know the files exist becuase when I retrieve one of the files (.pdf) and display it I am able to view the stored pdf file:
guard let urlString = UserDefaults.standard.string(forKey: "URL_PDF") else{return}
guard let pdfURL = URL.init(string: urlString) else{
print("no pdf URL")
return
}
print("pdfURL: \(pdfURL)")
guard let pdf = PDFDocument.init(url: pdfURL) else{
print("NO PDF DOCUMENT FOUND")
return
}
pdfPreview.document = pdf // CAN VIEW PDF FILE!!!
pdfPreview.autoScales = true
print("pdf document displayed!")
console:
pdfURL: file:///var/mobile/Containers/Data/Application/90ECD1AE-B9A5-46C9-AD30-C5D8D850A361/Documents/tempImagePDF.pdf
pdf document displayed!
How I generate my URLs:
// Create a URL to save PDF
func createPdfURL() -> URL {
let fileName = "tempImagePDF.pdf"
let documentsDirectories = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = documentsDirectories.first!
let pdfPageURL = documentDirectory.appendingPathComponent("\(fileName)")
return pdfPageURL
}
Then I save the PDF document as follows:
// SAVE PDF TO LOCAL FILE SYSTEM
func savePdfToLocaFile(pdf: PDFDocument) -> Void {
// CREATE URL FOR PDF DOCUMENT
let pdfURL = createPdfURL()
print("PDF SAVED TO URL: \(pdfURL)")
self.pdfDocumentURL = pdfURL
pdf.write(to: pdfURL)
}
I have also tried the following to retrieve the files by reconstructing the URL from the file name before using it to upload the files:
// CONSTRUCT URL FROM FILE NAME
let imgFileName = "tempImage_wb.jpg"
let pdfFileName = "tempImagePDF.pdf"
var dir: URL!
do {
dir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
} catch{
print("error re-creating url")
}
let imgURL = dir.appendingPathComponent(imgFileName)
let pdfURL = dir.appendingPathComponent(pdfFileName)
new console reading after implementing #vadian's suggestion:
PDF SAVED TO URL: file:///var/mobile/Containers/Data/Application/35C9D3EE-2D0C-4028-BCF3-2FE4581A0686/Documents/tempImagePDF.pdf
fileURL: file:///var/mobile/Containers/Data/Application/35C9D3EE-2D0C-4028-BCF3-2FE4581A0686/Documents/tempImagePDF.pdf
Please note the different application container identifier 7FF74F06-CD6E-47D6-850E-45E768C00D97 and 90ECD1AE-B9A5-46C9-AD30-C5D8D850A36.
Container locations change periodically therefore never save full paths pointing to the application container to UserDefaults.
Save only the file names and get the URL to the current document directory always with
let documentsFolderURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
Then append the file name with .appendingPathComponent
Note: The try! is safe because the document directory is always created by the framework.