BookmarkData 是什么?
bookmarkData(options:includingResourceValuesForKeys:relativeTo:)
是 URL
对象的实例方法,用于使用特定的选项为 URL
创建指向的目标资源创建书签数据。书签数据可以被保存到 UserDefaults
中或其他存储中,并在需要时还原为 URL
对象(即使用户将 URL
指向的资源进行了移动或重命名)。
使用 BookmarkData 持久化文件的访问权限
使用前需要确保应用程序启用了对应用沙盒外文件的访问权限,开启步骤参考下面的截图:
开启后根据下面的步骤持久化对文件夹的访问权限:
- 通过
fileImporter
读取目标文件夹并获取访问权限; - 通过
URL.bookmarkData(options:includingResourceValuesForKeys:relativeTo:)
方法创建书签(启用withSecurityScope
选项); - 使用
UserDefaults
存储该书签数据; - 应用程序重启后,从
UserDefaults
中读取BookmarkData
; - 使用
URL.init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:)
方法将BookmarkData
解析为URL
(启用withSecurityScope
选项); - 如果
bookmarkDataIsStale
返回的布尔值为 true,则需要使用bookmarkData(options:includingResourceValuesForKeys:relativeTo:)
重新创建书签,并更新应用程序中存储的书签版本; - 在解析后的
URL
上调用startAccessingSecurityScopedResource()
开始访问; - 使用
URL
访问目标文件; - 在解析后的
URL
上调用stopAccessingSecurityScopedResource()
停止访问。
下面的是一个使用 BookmarkData
持久化文件夹访问权限的例子:
import SwiftUI
struct BookmarkDataView: View {
@State var target: URL?
@State var selecting: Bool = false
@State var files: [URL] = []
var body: some View {
VStack {
HStack {
Text("目标路径" + (target?.path ?? "N/A"))
.padding()
Button("选择文件夹") {
selecting.toggle()
}.padding()
}
List(files, id: \.self) {file in
Text(file.path)
}.padding()
}
.onAppear {
if let url = loadBookmarkURL() {
scanTarget(at: url)
target = url
}
}
.fileImporter(isPresented: $selecting, allowedContentTypes: [.folder], allowsMultipleSelection: false, onCompletion: {
do {
let result = try $0.get()
if result.first != nil {
target = result.first!
saveBookmark(target: result.first!)
scanTarget(at: result.first!)
}
} catch {
print("\(error)")
}
})
}
// 保存 bookmarkData 到 UserDefaults
func saveBookmark(target: URL) {
do {
let bookmarkData = try target.bookmarkData(
options: [.withSecurityScope]
)
UserDefaults.standard.set(bookmarkData, forKey: "bookmark")
} catch {
print("书签数据保存失败:\(error)")
}
}
// 从 UserDefaults 中加载保存的 bookmarkData 并还原为文件路径
func loadBookmarkURL() -> URL? {
if let bookmarkData = UserDefaults.standard.data(forKey: "bookmark") {
do {
var isStale = false
let url = try URL(
resolvingBookmarkData: bookmarkData,
options: [.withoutUI, .withSecurityScope],
relativeTo: nil,
bookmarkDataIsStale: &isStale
)
return url
} catch {
print("书签数据解析失败 \(error)")
return nil
}
}
return nil
}
func scanTarget(at url: URL) {
let success = url.startAccessingSecurityScopedResource()
if !success {
print("加载失败")
return
}
do {
let fileManager = FileManager.default
files = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
} catch {
print("Error: \(error)")
}
url.stopAccessingSecurityScopedResource()
}
}
#Preview {
BookmarkDataView()
}
BookmarkData.swift
在上面的代码中,点击“选择文件夹”按钮后会出现 fileImporter
文件选择器,选择并授权后会将选中文件夹中的文件保存到 files
中,然后渲染这些文件夹的名称列表。
此时关闭应用并重启后应用会直接读取 UserDefaults
中保存的 BookmarkData
数据然后完成渲染,期间不需要用户手动选择并授权。
完整的代码可以在 Github 上查看。