我已经做了一个Finder extension
来添加一个菜单到Finder的上下文菜单中的任何文件。我想访问这个文件时,用户选择这个自定义菜单,显然这个文件,他们选择可以在任何地方的文件系统和允许的沙箱区域之外。
func accessFile(url: URL, userID: String, completion: @escaping ([String:Any]?, Error?) -> Void){
var bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: bookmarksPath) as? [URL: Data]
print("Testing if we have access to file")
// 1. Test if I have access to a file
let directoryURL = url.deletingLastPathComponent()
let data = bookmarks?[directoryURL]
if data == nil{
print("have not asked for access yet or directory is not saved")
// 2. If I do not, open a open dialog, and get permission
let openPanel = NSOpenPanel()
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = true
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = false
openPanel.prompt = "Grant Access"
openPanel.directoryURL = directoryURL
openPanel.begin { result in
guard result == .OK, let url = openPanel.url else {return}
// 3. obtain bookmark data of folder URL and store it to keyed archive
do{
let data = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
}catch{
print(error)
}
bookmarks?[url] = data
NSKeyedArchiver.archiveRootObject(bookmarks, toFile: bookmarksPath)
// 4. start using the fileURL via:
url.startAccessingSecurityScopedResource()
// < do whatever to file >
url.stopAccessingSecurityScopedResource()
}
}else{
// We have accessed this directory before, get data from bookmarks
print("we have access already")
let directoryURL = url.deletingLastPathComponent()
guard let data = bookmarks?[directoryURL]! else { return }
var isStale = false
let newURL = try? URL(resolvingBookmarkData: data, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
// 3. Now again I start using file URL and upload:
newURL?.startAccessingSecurityScopedResource()
// < do whatever to file >
newURL?.stopAccessingSecurityScopedResource()
}
}
当前它总是请求权限,因此书签不会被保存
1条答案
按热度按时间3yhwsihp1#
我不能100%确定这是否是问题的根源,但是我不知道你在哪里使用了
isStale
值。如果它从URL(resolvingBookmarkData:...)
返回true
,你必须重新创建/重新保存书签。所以在你的else
块中,你需要如下代码:当然,
data
现在需要是var
而不是let
。还要记住
stopAccessingSecurityScopedResource()
必须在 main 线程上调用,所以如果您不确定accessFile
是否在主线程上调用,您可能需要显式地这样做:你在两个地方都想这么做。
我喜欢在
URL
上写一个扩展,使它更好一点:所以我可以写:
不管你是否使用扩展,在
DispatchQueue.main
上显式调用stopAccessingSecurityScopedResource()
并不意味着直到下一次主运行循环迭代之前访问不会停止,这通常不是问题,但是如果你在一次运行循环迭代中多次启动和停止对同一个URL
的访问,它可能不起作用。因为它将多次调用startAccessingSecurityScopedResource()
,而其间没有stopAccessingSecurityScopedResource()
,并且在下一次迭代中,当执行排队的任务时,它将多次调用stopAccessingSecurityScopedResource()
。我不知道URL
是否维护了允许其安全的安全访问计数,或者只是一面旗帜,在这种情况下,它就不是了