I'm downloading a file using NSURLSessionDownloadTask which is working great. As I get my image or video file into a temp directory. But I need to move it to a permanent URL in order to put it into photos library. I'm using NSFileManager 's copyItemAtURL: without any success. Any reason it would throw? Perhaps file type is not compatible with documents directory?
let directory : String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
if let fileName = self.file.zOrigFileName {
let destinationPath = self.directory.stringByAppendingString("/\(fileName)")
if let destinationURL = NSURL(string: destinationPath) {
let fileManager = NSFileManager.defaultManager()
//IF file with same name exists delete it before copying new file
if fileAlreadyExistsAtURL(destinationURL) {
do {
try fileManager.removeItemAtURL(destinationURL)
} catch {
print("Error Removing Item At \(destinationURL.path)")
}
}
do {
try fileManager.copyItemAtURL(location, toURL: destinationURL)
self.saveURLToPhotosLibrary(destinationURL)
} catch {
//This is line always printing. my try statement always throwing.
print("Error Copying Item from \(location.path) to \(destinationURL.path)")
}
}
}
}
Here is the print statement. For security I'm replacing app bundle id from documents directory with $(AppId)
Error Copying Item from
Optional("/private/var/mobile/Containers/Data/Application/E8D9C365-15D2-40BD-B0B5-A000BEDA9F00/Library/Caches/com.apple.nsurlsessiond/Downloads/$(AppID)/CFNetworkDownload_CK3G3Z.tmp")
to
Optional("/var/mobile/Containers/Data/Application/E8D9C365-15D2-40BD-B0B5-A000BEDA9F00/Documents/shortMovie.mov")
You are using the wrong NSURL initializer. When working wit paths you need to use the fileURLWithPath initializer.
Related
I am trying to save favourites to document directory, the code works fine when I run it in simulator however when I build it to my device I am getting the following error.
You don’t have permission to save the file
“Documents.LikedDepartments” in the folder
“F36073C0-AC1E-46CA-BC1E-E03F9F316E1D”.
Also it might be worth noting that when I change use .cachesDirectory instead of .documentDirectory it works fine.
How can I grant write access for the documents directory ?
import Foundation
extension FileManager {
static var documentsDirectory: URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
print(paths)
return paths[0]
}
}
let savePath = FileManager.documentsDirectory.appendingPathExtension("LikedDepartments")
func save() {
let favourites = calculateFavourites()
do {
let data = try JSONEncoder().encode(favourites)
try data.write(to:savePath, options: [.atomic, .completeFileProtection])
} catch {
print("\(error)")
}
}
Cool file extension .LikedDepartments
Use appendingPathComponent instead.
https://developer.apple.com/documentation/foundation/nsstring/1417069-appendingpathcomponent
I know there are a few questions pertaining to this, but they're in Objective-C.
How can I access a .txt file included in my app using Swift on an actual iPhone? I want to be able to read and write from it. Here are my project files if you want to take a look. I'm happy to add details if necessary.
Simply by searching in the app bundle for the resource
var filePath = NSBundle.mainBundle().URLForResource("file", withExtension: "txt")
However you can't write to it because it is in the app resources directory and you have to create it in the document directory to write to it
var documentsDirectory: NSURL?
var fileURL: NSURL?
documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last!
fileURL = documentsDirectory!.URLByAppendingPathComponent("file.txt")
if (fileURL!.checkResourceIsReachableAndReturnError(nil)) {
print("file exist")
}else{
print("file doesnt exist")
NSData().writeToURL(fileURL!,atomically:true)
}
now you can access it from fileURL
EDIT - 28 August 2018
This is how to do it in Swift 4.2
var filePath = Bundle.main.url(forResource: "file", withExtension: "txt")
To create it in the document directory
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
let fileURL = documentsDirectory.appendingPathComponent("file.txt")
do {
if try fileURL.checkResourceIsReachable() {
print("file exist")
} else {
print("file doesnt exist")
do {
try Data().write(to: fileURL)
} catch {
print("an error happened while creating the file")
}
}
} catch {
print("an error happened while checking for the file")
}
}
Swift 3, based on Karim’s answer.
Reading
You can read files included in an app’s bundle through the bundle’s resource:
let fileURL = Bundle.main.url(forResource:"filename", withExtension: "txt")
Writing
However, you can’t write there. You will need to create a copy, preferably in the Documents directory:
func makeWritableCopy(named destFileName: String, ofResourceFile originalFileName: String) throws -> URL {
// Get Documents directory in app bundle
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
fatalError("No document directory found in application bundle.")
}
// Get URL for dest file (in Documents directory)
let writableFileURL = documentsDirectory.appendingPathComponent(destFileName)
// If dest file doesn’t exist yet
if (try? writableFileURL.checkResourceIsReachable()) == nil {
// Get original (unwritable) file’s URL
guard let originalFileURL = Bundle.main.url(forResource: originalFileName, withExtension: nil) else {
fatalError("Cannot find original file “\(originalFileName)” in application bundle’s resources.")
}
// Get original file’s contents
let originalContents = try Data(contentsOf: originalFileURL)
// Write original file’s contents to dest file
try originalContents.write(to: writableFileURL, options: .atomic)
print("Made a writable copy of file “\(originalFileName)” in “\(documentsDirectory)\\\(destFileName)”.")
} else { // Dest file already exists
// Print dest file contents
let contents = try String(contentsOf: writableFileURL, encoding: String.Encoding.utf8)
print("File “\(destFileName)” already exists in “\(documentsDirectory)”.\nContents:\n\(contents)")
}
// Return dest file URL
return writableFileURL
}
Example usage:
let stuffFileURL = try makeWritableCopy(named: "Stuff.txt", ofResourceFile: "Stuff.txt")
try "New contents".write(to: stuffFileURL, atomically: true, encoding: String.Encoding.utf8)
Just a quick update for using this code with Swift 4:
Bundle.main.url(forResource:"YourFile", withExtension: "FileExtension")
And the following has been updated to account for writing the file out:
var myData: Data!
func checkFile() {
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
let fileURL = documentsDirectory.appendingPathComponent("YourFile.extension")
do {
let fileExists = try fileURL.checkResourceIsReachable()
if fileExists {
print("File exists")
} else {
print("File does not exist, create it")
writeFile(fileURL: fileURL)
}
} catch {
print(error.localizedDescription)
}
}
}
func writeFile(fileURL: URL) {
do {
try myData.write(to: fileURL)
} catch {
print(error.localizedDescription)
}
}
This particular example is not the most flexible, but with a little bit of work you can easily pass in your own file names, extensions and data values.
🎁 Property Wrapper - Fetch and convert to correct data type
This simple wrapper helps you to load any file from any bundle in a cleanest way:
#propertyWrapper struct BundleFile<DataType> {
let name: String
let type: String
let fileManager: FileManager = .default
let bundle: Bundle = .main
let decoder: (Data) -> DataType
var wrappedValue: DataType {
guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found: \(name).\(type)") }
guard let data = fileManager.contents(atPath: path) else { fatalError("Can not load file at: \(path)") }
return decoder(data)
}
}
Usage:
#BundleFile(name: "avatar", type: "jpg", decoder: { UIImage(data: $0)! } )
var avatar: UIImage
You can define any decoder to match your needs
Get File From Bundle in Swift 5.1
//For Video File
let stringPath = Bundle.main.path(forResource: "(Your video file name)", ofType: "mov")
let urlVideo = Bundle.main.url(forResource: "Your video file name", withExtension: "mov")
Bundles are read only. You can use NSBundle.mainBundle().pathForResource to access the file as read-only, but for read-write access you need to copy your document to Documents folder or tmp folder.
Bundles can be written. You can use Bundle.main.path to overwrite file by adding it into Copy Bundles Resource.
I have to use a file from another bundle. So, following code worked for me. Needful when you work with a frameworks.
let bundle = Bundle(for: ViewController.self)
let fileName = bundle.path(forResource: "fileName", ofType: "json")
In my iOS app using swift, I annotated the pdf field and saved in the document directory. The view correctly display annotated field and the file is saved with annotation in document directory, however in the debugging area I am getting messages. The last method for two run was Ok but then also giving error message although displayed file and saved file in doc directory was fine. Any idea?
2020-02-14 21:51:23.245079-0800 PDFSaving[4092:1084625] [Unknown process name] Failed to load /System/Library/PrivateFrameworks/CorePDF.framework/Versions/A/CorePDF
2020-02-14 21:51:23.262963-0800 PDFSaving[4092:1084625] Error: Attempting to save dictionary with key: <CGPDFNameRef (/Encoding)>. Dictionary keys must be of type string.
2020-02-14 21:51:23.263062-0800 PDFSaving[4092:1084625] Error: Attempting to save dictionary with key: <CGPDFNameRef (/Font)>. Dictionary keys must be of type string.
2020-02-14 21:51:23.263113-0800 PDFSaving[4092:1084625] Error: Could not create dictionary value for key: /DR. Invalid value.
2020-02-14 21:51:23.263161-0800 PDFSaving[4092:1084625] Error: Cannot save value for annotation key: /DR. Invalid type.
I have used various techniques to save the pdf file but all are giving same message in the debugging area`
// 1st method of deleting and saving file
// File saving with errors in debugging area
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileNameWithPath = "\(path)/editedFile.pdf"
if FileManager.default.fileExists(atPath: getFilePath()){
do{
try FileManager.default.removeItem(atPath: getFilePath())
print("File path for deleted file: \(path)")
print("File is deleted successfully, path & filename of deleted file: \(fileNameWithPath)")
}catch{
print("error while deleting item from docDir :: \(error.localizedDescription)")
}
}
if let data = pdfDocument.dataRepresentation(){
let url = URL(fileURLWithPath: fileNameWithPath)
do{
try data.write(to: url)
print("pdf saved successfully \(fileNameWithPath)")
}catch{
print("error while saving file \(error.localizedDescription)")
}
}
// 2nd method of deleting and saving file
// File saving with errors in debugging area
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileNameWithPath = "\(path)/editedFile.pdf"
if FileManager.default.fileExists(atPath: getFilePath()){
do{
try FileManager.default.removeItem(atPath: getFilePath())
print("File path for deleted file: \(path)")
print("File is deleted successfully, path & filename of deleted file: \(fileNameWithPath)")
}catch{
print("error while deleting item from docDir :: \(error.localizedDescription)")
}
}
guard let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first,
let data = pdfView.document?.dataRepresentation() else {return}
let fileURL = url.appendingPathComponent(filename)
do {
try data.write(to: fileURL)
print("File path of saved file : \(path)")
print("pdf saved successfully, path and filename of saved file: \(fileNameWithPath)")
} catch {
print("error while saving file \(error.localizedDescription)")
}
// 3rd method of deleting and saving file
// Blank file is saving without any error in debugging area
let bundlePath = Bundle.main.path(forResource: "QW484A_2017R8", ofType: ".pdf")
let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let fileManager = FileManager.default
let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("editedFile.pdf")
if fileManager.fileExists(atPath: fullDestPath.path){
do {
try fileManager.removeItem(at: fullDestPath)
print("File path for removed file: \(destPath)")
print("File path & filename of file removed: \(fullDestPath)")
}
catch{
print("error while removing modified file: \(error.localizedDescription)")
}
}
do{
try fileManager.copyItem(atPath: bundlePath!, toPath: fullDestPath.path)
print("File path of saved file : \(destPath)")
print("database saved successfully, path and filename of saved file: \(fullDestPath.path)")
}catch{
print("error while saving modified file: \(error.localizedDescription)")
}
// 4th method of deleting and saving
// File saving with errors in debugging area
let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let fileManager = FileManager.default
let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("editedFile.pdf")
if fileManager.fileExists(atPath: fullDestPath.path){
do {
try fileManager.removeItem(at: fullDestPath)
print("File path for removed file: \(destPath)")
print("File path & filename of file removed: \(fullDestPath)")
}
catch{
print("error while removing modified file: \(error.localizedDescription)")
}
}
self.pdfDocument.write(to: fullDestPath)
print("The save filed path and name: \(fullDestPath)")
// 5th method of saving
// File saving with errors in debugging area
guard let url2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first,
let data = pdfView.document?.dataRepresentation() else {return}
let fileURL = url2.appendingPathComponent("editedfile.pdf")
do {
try data.write(to: fileURL)
print("print path: \(fileURL)")
} catch {
print(error.localizedDescription)
}
// 6th method
let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let fileManager = FileManager.default
let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("editedFile.pdf")
if fileManager.fileExists(atPath: fullDestPath.path){
do {
try fileManager.removeItem(at: fullDestPath)
print("File path for removed file: \(destPath)")
print("File path & filename of file removed: \(fullDestPath)")
}
catch{
print("error while removing modified file: \(error.localizedDescription)")
}
}
self.pdfView.document?.write(to: fullDestPath)
print("The save filed path and name: \(fullDestPath)")
`
So finally I work around this issue. I thought to update as it may be hurting others. There two methods used for PDF form generation, ist form was created in word and than transferred to PDF with annotations, in 2nd method PDF form was generated using Acro Form, both were giving above problem. In 3rd attempt I created the the PDF form using Adobe Acrobat DC from scratch and tried, this time no problem in saving the PDF with annotations, seems this is the issue with format transformation
How can I copy and paste the entire content of a folder using Swift on a OS X? If the destinationPath already contains the folder, than it should replace it.
I tried
let appSupportSourceURL = NSURL(string: appSupportSourcePath)
let appSupportDestinationURL = NSURL(string: appSupportDestinationPath+"/"+appSupportFileName)
if (fileManager.isReadableFileAtPath(appSupportSourcePath)){
do {
try fileManager.copyItemAtURL(appSupportSourceURL!, toURL: appSupportDestinationURL!)}
catch{
}
}
but I realised, that this only works for files. I am trying to replace a whole folder.
I know Apple encourages new code to use the URLs to specify file system path. However NSFileManager is an old class and it's still in transition between the old string-based path and the new URL-based paradigm. Try this:
let appSupportSourcePath = "..."
let appSupportDestinationPath = "..."
let fileManager = NSFileManager.defaultManager()
do {
// Delete if already exists
if fileManager.fileExistsAtPath(appSupportDestinationPath) {
try fileManager.removeItemAtPath(appSupportDestinationPath)
}
try fileManager.copyItemAtPath(appSupportSourcePath, toPath: appSupportDestinationPath)
} catch {
print(error)
}
Edit: method with NSURL
let appSupportSourceURL = NSURL(fileURLWithPath: "...", isDirectory: true)
let appSupportDestionURL = NSURL(fileURLWithPath: "...", isDirectory: true)
try! NSFileManager.defaultManager().copyItemAtURL(appSupportSourceURL, toURL: appSupportDestionURL)
I'm trying to count the number of directories in the iOS Documents Directory of my app using the following swift code:
let manager = NSFileManager.defaultManager()
let path: NSString = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("Documents")
var array = manager.contentsOfDirectoryAtPath(path, error: nil)
let count = array?.count
My Documents directory currently contains one directory but when I run this code, 'count' returns nil every time. Why is this?
Your method to find the Documents directory is wrong, NSBundle.mainBundle().resourcePath gives the path to the application bundle.
There is a dedicated function for this purpose:
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
And then you should use the error parameter and check the return value when
retrieving the directory contents:
let fileManager = NSFileManager.defaultManager()
var error : NSError?
if let files = fileManager.contentsOfDirectoryAtPath(documentsPath, error: &error) {
let count = files.count
// ...
} else {
println("Could not get contents of directory: \(error?.localizedDescription)")
}
Credit to Martin R. for the original answer! Here is a Swift 3 version incase anyone needs it...
Swift 3
func countFilesInDocumentsDirectory() {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) [0] as String
do {
let files = try FileManager.default.contentsOfDirectory(atPath: documentsPath)
print("File count = \(files.count)")
for i in 0...files.count-1 {
print("FILE: \n\(files[i])")
}
} catch {
//...
}
}