需要解决的问题
macOS 通过 NSOpenPanel 选择需要访问的文件或者文件夹后,当退出项目后,再次启动时,若再想访问之前的选择的文件或者文件夹,如果没有对文件或者文件夹的 URL 做额外的处理,App 是没有访问权限的。
解决方法
将 URL 进行 Security-Scoped Bookmarks 处理保存在沙盒。
一、项目需要的设置
选择项目,TARGETS -> Signing & Capabilities -> App Sandbox,对选择的文件进行权限选择。 User Selected File 选择 Read Only 或者 Read/Write。
二、这里以打开 PDF 文件为例,保存 URL
// PDF 文件的 URL
var fileURL: URL?
// MARK: 选择文件
@IBAction func selectFileEvent(_ sender: Any) {
let openFilePanel = NSOpenPanel()
openFilePanel.allowedFileTypes = ["pdf"]
openFilePanel.allowsMultipleSelection = false
openFilePanel.canChooseFiles = true
openFilePanel.canCreateDirectories = false
openFilePanel.canChooseDirectories = false
openFilePanel.begin { (response) in
if response == .OK {
if let url = openFilePanel.urls.first {
self.storeBookMark(url: url)
self.fileURL = url
}
}
}
}
保存 URL,通过 bookmarkData(options:includingResourceValuesForKeys:relativeTo:) 方法,将获取的 Data 持久化保存
// MARK: 保存 url bookmark
private func storeBookMark(url: URL) {
do {
let urlData = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
// 可以保存到数据库,这里通过 UserDefaults 保存
UserDefaults.standard.set(urlData, forKey: "save_file_url")
} catch {
print(error)
}
}
三、恢复 URL 的访问权限
当 App 再次启动的时候,在访问之前的文件或者文件夹时,需要从保存的位置读取 URL
// MARK: 加载保存的文件 URL
private func loadBookMark() {
let urlData = UserDefaults.standard.object(forKey: "save_file_url") as! Data
var isStale = false
do {
let restoredURL = try URL(resolvingBookmarkData: urlData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
if isStale {
// 如果数据已过期,重新请求权限
_ = restoredURL.startAccessingSecurityScopedResource()
}
fileURL = restoredURL
} catch {
}
}
// MARK: 显示 PDF 文件
private func setupPDFView() {
if let url = fileURL {
// 请求权限
_ = url.startAccessingSecurityScopedResource()
// 这个方法可以检查是否有访问权限
if FileManager.default.isReadableFile(atPath: url.path) {
print("can access")
}
let pdfDocument = PDFDocument(url: url)
pdfView.document = pdfDocument
pdfView.autoScales = true
pdfView.scaleFactor = 1.0
view.addSubview(pdfView, positioned: .below, relativeTo: topView)
pdfView.snp.makeConstraints { make in
make.top.equalTo(topView.snp.bottom)
make.left.right.bottom.equalToSuperview()
}
url.stopAccessingSecurityScopedResource()
}
}