I'm experiencing problems (on Mojave and Catalina) with "reusing" security scope URL bookmark for a folder between app launches in my app.
It's simple decompressing application using libarchive
framework. User selects file to decompress, I want to store URL bookmark for it's parent folder (e.g. ~/Desktop), and reuse it next time user tries to decompress file in the same folder.
First, I added following to my app's entitlements file:
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
When accessing file (parent folder respectively) for the first time:
- User selects file to decompress
- I present
NSOpenPanel
to obtain access to the file folder:
let directoryURL = fileURL.deletingLastPathComponent()
let openPanel = NSOpenPanel()
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = true
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = false
openPanel.prompt = "Grant Access"
openPanel.directoryURL = directoryURL
openPanel.begin { [weak self] result in
guard let self = self else { return }
// WARNING: It's absolutely necessary to access NSOpenPanel.url property to get access
guard result == .OK, let url = openPanel.url else {
// HANDLE ERROR HERE ...
return
}
// We got URL and need to store bookmark's data
// ...
}
- I obtain bookmark data of folder URL and store it to keyed archive:
let data = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
bookmarks[url] = data
NSKeyedArchiver.archiveRootObject(bookmarks, toFile: bookmarksPath)
- Now I start using file URL and use
libarchive
to decompress .zip file to it's parent folder:
fileURL.startAccessingSecurityScopedResource()
// Decompressing file with libarchive...
fileURL.stopAccessingSecurityScopedResource()
- Everything is working as expected, .zip file gets decompressed
When relaunching app, decompressing file in the same folder, reusing saved bookmark data:
- I get bookmarks from keyed archive:
let bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: bookmarksPath) as? [URL: Data]
- I get bookmark data from bookmarks for the file's parent folder and resolve it:
let directoryURL = fileURL.deletingLastPathComponent()
let data = bookmarks[directoryURL]!
var isStale = false
let newURL = try URL(resolvingBookmarkData: data, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
- Now again I start using file URL and use
libarchive
to decompress .zip file to it's parent folder:
fileURL.startAccessingSecurityScopedResource()
// Decompressing file with libarchive...
fileURL.stopAccessingSecurityScopedResource()
But this time libarchive
returns error saying Failed to open \'/Users/martin/Desktop/Archive.zip\'
I know I might be doing something terribly wrong or not understanding concept of security scoped URL bookmarks but can't find where's the problem. Any hints?
FINAL SOLUTION
Both Rckstr's answer and answer in this Apple developer forum thread pointed me into the right direction. It's absolutely necessary to call startAccessingSecurityScopedResource()
on THE SAME INSTANCE of URL returned by try URL(resolvingBookmarkData: data, options: .withSecurityScope ...