Permission error when saving to documentDirectory - swift

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

Related

Swift deleting file from folder in documents directory

I appreciate some assistance. I created a folder inside my documents directory named "MyPhotos". I added three image files to the folder. No issues.
Next I'm attempting to construct some code that will delete the files when needed but can't get it right. I can append the MyPhotos folder to the search path but can't subsequently append the file name to the path after multiple tries. tempPhotoTitle is a variable. Thank you for helping.
let fileManager = FileManager.default
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask,true)[0] as NSString
let destinationPath = documentsPath.appendingPathComponent("MyPhotos")
let finalPath = destinationPath.appending(tempPhotoTitle)
do {
try fileManager.removeItem(atPath: finalPath)
}
catch {
print(error)
}
The issue there is that you are working with strings (paths) and not adding the slash. I recommend always working with URLs this way you dont need to worry about adding the slash to your path.
do {
let tempPhotoTitle = "filename.jpg"
let documents = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let photos = documents.appendingPathComponent("MyPhotos")
let fileURL = photos.appendingPathComponent(tempPhotoTitle)
try FileManager.default.removeItem(at: fileURL)
} catch {
print(error)
}
Leo Dabus's answer of working with URLs is the better one. However, it's worth mentioning that when working with Strings, if you bridge to NSString again, appendingPathComponent is available:
let finalPath = (destinationPath as NSString).appendingPathComponent(tempPhotoTitle)

Why can't I get the filePath although I save my file properly and see it?

I am trying to write/read to the bundle and I use the below reference from StackOverflow:
How to access file included in app bundle in Swift?
which looks like good code and should work fine.
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")
}
}
To get filepath:
var filePath = Bundle.main.url(forResource: "file", withExtension: "txt")
I can save the file but somehow I can never find the file path by
var filePath = Bundle.main.url(forResource: "file", withExtension: "txt")
I cannot get any of the extensions. I even can see the saved file in my app's data under Devices in XCode which looks great but I can't find it by filePath​. Can you please help me?
Well, let's look at it this way:
Bundle.main is your app; Bundle.main.url looks for the file inside your app. (That's why the linked answer is called "How to access file included in app bundle in Swift?")
The documentDirectory is outside your app, namely (get this), it's the Documents directory.
So you are saving the file outside your app and then looking for the file inside your app. That's not where it is, so that's not going to work.

How can I find the path of iOS app internal directory in Swift?

How can I find the path of iOS app internal directory in Swift?
Firstly, what is the equivalent name of android app internal directory in iOS app?
Secondly, how can I find the path of the directory?
which api should I use in Swift?
My main purpose is trying to copy Bundle sqlite file to the directory
To get path of document directory you can use this code.
// path to documents directory
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
if let documentDirectoryPath = documentDirectoryPath {
}
For more information follow these links : File System Basics and Accessing Files and Directories
Try this -
public func getDatabasePath() -> String {
let databaseURL = try? FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("YOUR_DB_FILE_NAME.sqlite")
// If DB file not present in file manager copy DB file to user document directory
let fileManager = FileManager.default
if fileManager.fileExists(atPath: (databaseURL?.path)!) {
print("FILE AVAILABLE")
return (databaseURL?.path)!
} else {
print("FILE NOT AVAILABLE ... Copying")
if fileManager.createFile(atPath: (databaseURL?.path)!, contents: Data(), attributes: nil) {
return (databaseURL?.path)!
}
}
return ""
}

Issue with loading file from disk

Ok so this one has had me scratching my head for a while.
I have a png file that I write out to disk. I get the data by:
let data = UIImagePNGRepresentation(scaledImage!)
let filename = getDocumentsDirectory().appendingPathComponent("\(record.uid!).png")
I do a try catch and everything seems to work. The resulting filename is:
file:///var/mobile/Containers/Data/Application/C6B796E8-2DB6-45A4-9B18-EF808B8CA3CA/Documents/580420d51800cd826a7e217c.png
The problem comes when I try to load that image back from the disk.
When I get a list of all files in the documents directory I get:
[file:///private/var/mobile/Containers/Data/Application/C6B796E8-2DB6-45A4-9B18-EF808B8CA3CA/Documents/580420d51800cd826a7e217c.png]
The only difference I can see is the 'private' part of the filepath. When I try to check to see if the file exists using the filepath I get back from appending the filename (the one without the private part) I get false.
What am I missing?
Swift 3/4 code
Let us assume the method getDocumentsDirectory() is defined as follows
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
In order to save the image
let data = UIImagePNGRepresentation(scaledImage!)
let filename = getDocumentsDirectory().appendingPathComponent("\(record.uid!).png")
try? data?.write(to: filename)
And your image is saved in Documents Directory
Now in order to load it back
let imagePath = getDocumentsDirectory().appendingPathComponent("\(record.uid!).png").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: imagePath){
print("Image Present")
//load it in some imageView
}else {
print("No Image")
}

Copying file from temp url to documents directory always throws. NSURLSessionDownloadTask

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.