NSFileManager moveItem throws Error Code=513 - ios

I'm trying to move a file from a URL to another, however I get an error with code 513. I understand that this is a NSFileWriteNoPermissionError. I don't see how this possible considering I created the folder in the first place.
//self.video!.folderURL.path = /var/mobile/Containers/Data/Application/066BDB03-FD47-48D8-B6F8-932AFB174DF7/Documents/AV/769c504203024bae95b47d78d8fe9029
try? fileManager.createDirectory(atPath: self.video!.folderURL.path, withIntermediateDirectories: true, attributes: nil)
// Update permission for AV folder
do {
let parentDirectoryPath = self.video!.folderURL.deletingLastPathComponent().path
let result = try fileManager.setAttributes([FileAttributeKey.posixPermissions: 0o777], ofItemAtPath: parentDirectoryPath)
print(result)
} catch {
print("Error = \(error)")
}
// sourceURL = file:///private/var/mobile/Containers/Data/PluginKitPlugin/50D8B8DB-19D3-4DD6-93DD-55F37CF87EA7/tmp/trim.6D37DFD2-5F27-4AB9-B478-5FED8AA6ABD7.MOV
// self.url = file:///var/mobile/Containers/Data/Application/066BDB03-FD47-48D8-B6F8-932AFB174DF7/Documents/AV/0b0b6803e891780850152eeab450a2ae.mov
do {
try fileManager.moveItem(at: sourceURL, to: self.url)
} catch {
QLogTools.logError("Error moving video clip file \(error)")
}
Error
Error Domain=NSCocoaErrorDomain Code=513 "“trim.90A10B33-B884-419F-BAD9-65583531C3C3.MOV” couldn’t be moved because you don’t have permission to access “AV”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/PluginKitPlugin/0849234B-837C-43ED-BEDD-DE4F79E7CE96/tmp/trim.90A10B33-B884-419F-BAD9-65583531C3C3.MOV, NSUserStringVariant=(
Move
I tried altering the permission of the folder I created in the first place to 0o777, but when I print out its attributes it returns 511 as its permissions.
P.S:This only happens on iOS 13.

I solved my issue, but it may be different to what you were experiencing: I still had issues when changing to copyItem. I'm adding my findings here as an answer in case anyone else stumbles upon this question as I did and it helps them out.
My app loads custom files that can be downloaded from my website. With iOS 13's download manager, these are copied to the Downloads folder first, and can then be opened by my app. Yet my app was having permission errors when attempting to access these files in the same way as iOS 12.
This answer to a different question clued me in. I need to call startAccessingSecurityScopedResource() and stopAccessingSecurityScopedResource() as follows:
// source and destination are URLs
if source.startAccessingSecurityScopedResource() {
try FileManager.default.moveItem(at: source, to: destination)
}
source.stopAccessingSecurityScopedResource()
The Apple documentation doesn't really suggest that this is new with iOS 13 but I think just the different approach to file downloading means sandboxing considerations need to be made.

Related

Save file using Swift and iOS

I have an iOS app and I need to be able to pick a file on my iCloud Drive, modify the file, and save the modified file with a new extension. I've tried lots of things but I still can't write the new file. Here is my latest code:
documentPickerViewController = DocumentPickerViewController(documentTypes: ["public.item"], in: .open)
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
let fileURL = urls[0]
do {
// Read the file
_ = fileURL.startAccessingSecurityScopedResource()
let fileData = try Data(contentsOf: fileURL, options: .uncached)
fileURL.stopAccessingSecurityScopedResource()
// write the file
let fileCopyURL = fileURL.appendingPathExtension("copy")
_ = fileCopyURL.startAccessingSecurityScopedResource()
try fileData.write(to: fileCopyURL)
fileCopyURL.stopAccessingSecurityScopedResource()
}
catch {
print(error.localizedDescription)
}
}
When I pick a file on my iCloud Drive I get the following error:
You don’t have permission to save the file “TestFile.txt.copy” in the folder “Test Files”.
How can a save the modified file?
First, I think you should make sure that you have the correct entitlements/permissions to write to iCloud Drive:
If this still doesn't work, try updating the CFBundleVersion or Build Number of your app and make sure that your Bundle Identifier (in the first section of Signing & Capabilities) is correctly registered on your Apple Developer account.
According to your code, what you're trying to accomplish is feasible but the issue comes from elsewhere.
Let me know if you have an issue following these steps. Good Luck!

Alternative UIActivityViewController save dialog for Mac Catalyst or solution for UIDocumentPickerViewController throwing error code 260

I am looking for an alternative export menu other then UIActivityViewController for a Mac Catalyst app. While this works, the user can not choose where they want to save the file (the file is a JSON file of all the items in a list) and I would like the user to be able to choose the directory they want to save the JSON to. I have tried the following code but it gives the error "Error Domain=NSCocoaErrorDomain Code=260 'The file 'name.json' couldn’t be opened because there is no such file'" when you try to save a file.
The Code:
let fileManager = FileManager.default
do {
let fileURL2 = fileManager.temporaryDirectory.appendingPathComponent("\(detailedList.lname!).json")
// Write the data out into the file
try jsonData.write(to: fileURL2)
// Present the save controller. We've set it to `exportToService` in order
// to export the data -- OLD COMMENT
let controller = UIDocumentPickerViewController(url: fileURL2, in: UIDocumentPickerMode.exportToService)
present(controller, animated: true) {
// Once we're done, delete the temporary file
try? fileManager.removeItem(at: fileURL2)
}
} catch {
print("error creating file")
}
I have tried Googling other ways or ways to get this to work but I cannot find anything that will work on Mac Catalyst. I know you can do this because I have seen other apps and examples do it but nothing works for me. So what would be a possible alternative way of doing this or a solution to this code?
The problem is that you are removing the file you wish to save before the user has a chance to choose where they want to save it.
The completion handler where you call try? fileManager.removeItem(at: fileURL2) is called as soon as the document picker is displayed.
The proper solution is to delete the file in the UIDocumentPickerDelegate methods, not when the picker is presented.

initiaize object of AGSGeodatabase with file path is returning "File not found

I want to post this question on esri community, but I was not able to login there.
I created the offline geo database and showing the layers from it to the map.
My issue is when i download the geoDatabse and show the data from it is working fine for the first time.
But when I restart the app and create the AGSGeodatabase object from path then the "self.generatedGeodatabase.load" function gives error
Error Domain=com.esri.arcgis.runtime.error Code=14 "File not found" UserInfo={NSLocalizedFailureReason=, NSLocalizedDescription=File not found, Additional Message=}
my created url is :-
file:///var/mobile/Containers/Data/Application/60CF7AE1-2DE1-4FA0-805A-6140D4829E89/Documents/GeoDatabase/2018-05-134T18:29:01+0530.geodatabase
and I'm creating object is like this :-
if let geoDatabasePath = URL(string: SettingsManager.geoDatabasePath) {
self.generatedGeodatabase = AGSGeodatabase(fileURL: geoDatabasePath)
self.displayLayersFromGeodatabase()
}
and error is coming when this function runs:-
self.generatedGeodatabase.load(completion: { [weak self] (error:Error?) -> Void in
if let error = error {
print(error)
}
})
Previously I was just saving the path of the geoDatabse to preference (when creating the geoDatabse) and app is not detecting the geoDatabse at that location when I restart the app even the file is there.
The solution to this problem I find is to detect the geoDatabse path at runtime and create path using FileManager. Then it works.

iOS11: Can't create folder again in iCloud drive once deleted manually

I have developed an app for saving a file to iCloud drive from the app.
I am saving a file from my app to iCloud drive. So its working as expected. Its creating my app folder with saved file in iCloud drive app.
But if I delete the folder and try to save the file again from my app,
its giving an error as below :
"file.txt" couldn't be moved to "Documents" because either the former doesn't exist, or the folder containing the later doesn't exist.
This issue I'm facing only in iOS11. In iOS 10 its working properly.
How to fix this issue. Thanks in advance.
I found that you need to re-create the folder if it gets deleted, it won't create it automatically. So I'd try this:
let fileManager = FileManager.default
let iCloudPath = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("DirectoryName")
do {
// Try to create the file here
} catch let error {
// If that operation fails, you print the error and create the folder again
print(error.localizedDescription)
do {
try fileManager.createDirectory(atPath: iCloudPath!.path, withIntermediateDirectories: true, attributes: nil) }
catch let error {
print("Unable to create directory \(error.localizedDescription)") }
}

iOS 9 read file permission

In iOS 9+ I get a nil on any attempt to read from file. The file in this case is a image file path.
using
NSData(contentsOfFile: stringpath, options: NSDataReadingOptions.DataReadingUncached)
or
NSData(contentsOfFile: stringpath)
Actions:
I have removed the "file://" from the path and it now has a permissions issue.
Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_0048.JPG” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/var/mobile/Media/DCIM/100APPLE/IMG_0048.JPG, NSUnderlyingError=0x13f978f50 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I have added the NSAllowArbitraryLoads and set it to true.
I have tried to look for the file myself using "NSSearchPathDirectory" however the paths do not match in any way
I encountered this error because I was attempting to access multiple files in the same block. The fix that worked for me was changing the code structure such that each file url was obtained, then read from, before attempting to get the next file url.
You are most likely getting this error, because iOS apps only have access to files within its sandbox. See Apple documentation on file systems for details.
In your Application, you don't have permission to access the file of /var/mobile/Media/DCIM/100APPLE/IMG_0048.JPG, because of Sandboxing.
So, whatever you do, you can't initialize NSData or UIImage with the file path. But you can access the file of /var/mobile/Media/DCIM/100APPLE/xxx.mov with AVURLAsset. In my application, I extracted the data rather than URL from gallery by Photos kit and initialized UIImage with the data.
PHImageManager.default().requestImageData(
for: assetObject!, options: options,
resultHandler: {
data, _, _, _ in
if data != nil {
self.assetUrl = movieMaker.createMovieFrom(imageData: data!, duration: Int(CXPreparetValue.imageDuration))
}
})
it works for me! If you have other opinions, please tell me.
In my case, the file permissions were too restrictive, so I couldn't read the file.
Adding read+write permissions to the file before accessing it solved it.
do {
// Retrieve any existing attributes
var attrs = try FileManager.default.attributesOfItem(atPath: stringpath)
let existing = (attrs as NSDictionary).filePosixPermissions()
// Set the read+write value in the attributes dict
attrs[.posixPermissions] = existing | 0b110000000
// Update attributes
try FileManager.default.setAttributes(attrs, ofItemAtPath: stringpath)
// Read data from file
let data = try Data(contentsOf: URL(fileURLWithPath: stringpath, isDirectory: false), options: .uncached)
print("success: \(data.count)")
} catch {
print(error)
}
That works if you're in a folder with enough permissions, as you can change the files permissions even if you didn't had read permission on the file previously. This solution was applied at https://github.com/ZipArchive/ZipArchive/issues/293.

Resources