经过一番研究,我偶然发现了这个苹果文档页面(当我发布这个问题时,经过几个小时的谷歌搜索后没有找到该页面)
https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories
导航至Save the URL as a Bookmark
文章的一部分。
使用 SwiftUI fileImporter 可以对用户选择的目录进行一次性读/写访问。为了保留这种读/写访问权限,我必须制作一个书签并将其存储在某个地方以便稍后访问。
由于我只需要用户下载目录的一个书签,因此我将其保存在 UserDefaults 中(书签的大小非常小)。
保存书签时,该应用程序会添加到Files and folders
在用户的设置中,因此用户可以立即撤销应用程序的文件权限(因此我的代码片段中的所有防护语句)。
这是我使用的代码片段,经过测试,下载确实在应用程序启动和多次下载时持续存在。
import SwiftUI
import UniformTypeIdentifiers
import Alamofire
struct ContentView: View {
@AppStorage("bookmarkData") var downloadsBookmark: Data?
@State private var showFileImporter = false
var body: some View {
VStack {
Button("Set downloads directory") {
showFileImporter.toggle()
}
Button("Save to downloads directory") {
Task {
do {
let destination: DownloadRequest.Destination = { _, response in
// Save to a temp directory in app documents
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("Downloads")
let suggestedName = response.suggestedFilename ?? "unknown"
let fileURL = documentsURL.appendingPathComponent(suggestedName)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
let tempUrl = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value
// Get the bookmark data from the AppStorage call
guard let bookmarkData = downloadsBookmark else {
return
}
var isStale = false
let downloadsUrl = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
guard !isStale else {
// Log that the bookmark is stale
return
}
// Securely access the URL from the bookmark data
guard downloadsUrl.startAccessingSecurityScopedResource() else {
print("Can't access security scoped resource")
return
}
// We have to stop accessing the resource no matter what
defer { downloadsUrl.stopAccessingSecurityScopedResource() }
do {
try FileManager.default.moveItem(at: tempUrl, to: downloadsUrl.appendingPathComponent(tempUrl.lastPathComponent))
} catch {
print("Move error: \(error)")
}
} catch {
print("Downloading error!: \(error)")
}
}
}
}
.fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in
switch result {
case .success(let url):
// Securely access the URL to save a bookmark
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
// We have to stop accessing the resource no matter what
defer { url.stopAccessingSecurityScopedResource() }
do {
// Make sure the bookmark is minimal!
downloadsBookmark = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
} catch {
print("Bookmark error \(error)")
}
case .failure(let error):
print("Importer error: \(error)")
}
}
}
}