I am using the following code in the AppDel. This is triggered when a user taps on a gpx file or uses the share option to share the file with my app. At this point it is the user that specified that they are allowing my app to access the file so I'm a little confused a to why this is still being denied. Any advice much appreciated.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(url.lastPathComponent)
if FileManager.default.fileExists(atPath: fileURL.path) {
print("File already exists")
}
else {
do {
try FileManager.default.copyItem(at: url, to: fileURL)
print("Did write file to disk")
}
catch {
DispatchQueue.main.async(){
print("Catch error writing file to disk: \(error)")
}
}
}
}
return true
}
The error prints to the console as follows:
Error Domain=NSCocoaErrorDomain Code=257 "The file “Beech_Hill_Long_Route.gpx” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Beech_Hill_Long_Route.gpx, NSUnderlyingError=0x282c19a70 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I'm less knowledgeable about exactly how this works on iOS, but I do have some knowledge about how it works for macOS, and the two should be similar. For macOS sandboxed apps the user has to select the file outside of the sandbox specifically via
NSOpenPanelorNSSavePanel. The equivalent on iOS would beUIDocumentPickerViewController, but I assume an explicit share would work too. Based on that, here's how I would think about it and what I would try, if I were faced with your problem:You've got two
URLs involved in your call toFileManager.default.copy(). It's not clear which one is producing the error, so I'll consider both.First let's look at
fileURL. You're constructing it to put a file in the user'sDocumentsdirectory. On macOS at least, that directory is not in the app's sandbox, which means asking the user viaNSSavePanel(which also means they might decide to put it somewhere else). You might have to do theUIKitequivalent, or just make sure you're picking a location that is in your sandbox.To test that, instead of doing the copy, try writing to
fileURLto isolate just that one. For example:If that fails, then your problem is with
fileURL, in which case you may need to useUIDocumentPickerViewControllerto save it, or pick a location that's definitely in the app's sandbox.If the test succeeds, the problem must be with the incoming
URL.I'm going to assume that
urlis already security scoped, because I'm not sure how sharing would even work otherwise. What I think is most likely happening behind the scenes is that when the user shares aURLwith your app, iOS creates a security-scoped bookmark from theURLand sends the bookmark rather than theURLto your app. Then on your app's end, that bookmark is used to reconstitute theURLbefore passing it on to your app's delegate. If I'm right about that you'll need to open and close a security scope to use it:Note that
stopAccessingSecurityScopeResource()has to be called on the main thread, so if you're code is happening asynchronously you'll need to schedule it to run there:If you need to save the
URLitself to use in a future run of your program... you can't. Well, you can, but it won't be valid, so you'll be right back to permissions errors. Instead you have to save a bookmark, and then in that future run, reconstruct theURLfrom the bookmark.bookmarkis an instance ofData, so you can write that toUserDefaultsor wherever you might want to save it. To get aURLback from it later:Note that if
isStaleistrueafter the initializer returns, then you need to remake and resave the bookmark.It's a shame that we have to go through this much trouble, but we live in a world where some people insist on doing bad things to other people's devices and data, so it's what we have to deal with the protect users from malicious data breaches.