swift - 将文本文件保存到选定的目录,并在应用关闭后保留选定的目录
问题描述
我试图让用户选择将文件保存在他/她选择的目录中。当我弹出一个NSOpenPanel()
让用户选择目录时,我可以在那里保存一个文本文件。但是,如果我保存该路径,然后尝试在其中保存更多文件而不再次打开该路径NSOpenPanel()
,则会出现错误。是的,我知道应用程序的沙盒性质,因此我要求用户选择一个文件夹。
如何将文件保存到用户选择的目录,并确保以后应用程序可以继续将文件保存在那里。
这是我尝试过的代码。
打开面板:
func saveNewFile(filename: String) {
let contents = "Some text..."
@AppStorage("filesDirectory") var filesDirectory: String = ""
print("saving in: \(filesDirectory)")
do
{
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = true
panel.canChooseFiles = false
if panel.runModal() == .OK {
filesDirectory = panel.url?.path ?? "<none>"
}
let directoryURL: URL = panel.url!
let documentURL = directoryURL.appendingPathComponent (filename + ".txt")
print(documentURL)
try contents.write (to: documentURL, atomically: false, encoding: .utf8)
}
catch
{
print("An error occured: \(error)")
}
}
没有它
func saveNewFile(filename: String) {
let contents = "Some text..."
@AppStorage("filesDirectory") var filesDirectory: String = ""
print("saving in: \(filesDirectory)")
do
{
/*let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = true
panel.canChooseFiles = false
if panel.runModal() == .OK {
filesDirectory = panel.url?.path ?? "<none>"
}*/
let directoryURL: URL = URL(string: filesDirectory)!
let documentURL = directoryURL.appendingPathComponent (filename + ".txt")
print(documentURL)
try contents.write (to: documentURL, atomically: false, encoding: .utf8)
}
catch
{
print("An error occured: \(error)")
}
}
解决方案
我不熟悉@AppStorage
. 这是一个安全范围书签的例子UserDefaults.standard
首先创建一个自定义错误和一个方便的方法来可靠地访问安全范围。
enum ResolveError : Error { case cancelled }
extension URL {
func accessSecurityScopedResource<Value>(at url : URL, accessor: (URL) throws -> Value) rethrows -> Value {
let didStartAccessing = startAccessingSecurityScopedResource()
defer { if didStartAccessing { stopAccessingSecurityScopedResource() }}
return try accessor(url)
}
}
该方法看起来很复杂,但事实并非如此。该defer
表达式确保以受控方式保留安全范围,并且该throws - rethrows
表达式允许在闭包中运行非抛出和抛出代码,您甚至可以通过注释类型从闭包中返回一个值
let numberOfPages = baseURL.accessSecurityScopedResource(at: fileURL) { url -> Int in
guard let pdfDocument = PDFDocument(url: url) else { return 0 }
return pdfDocument.pageCount
}
然后创建一个方法来解析书签数据并在丢失数据的情况下显示打开的对话框
func resolveURL(for key: String) throws -> URL {
if let data = UserDefaults.standard.data(forKey: key) {
var isStale = false
let url = try URL(resolvingBookmarkData: data, options:[.withSecurityScope], bookmarkDataIsStale: &isStale)
if isStale {
let newData = try url.bookmarkData(options: [.withSecurityScope])
UserDefaults.standard.set(newData, forKey: key)
}
return url
} else {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = true
panel.canChooseFiles = false
if panel.runModal() == .OK,
let url = panel.url {
let newData = try url.bookmarkData(options: [.withSecurityScope])
UserDefaults.standard.set(newData, forKey: key)
return url
} else {
throw ResolveError.cancelled
}
}
}
注意:isStale
参数的行为非常脆弱。您可能会添加更精细的错误处理。
要将一些内容保存到安全范围内的文件中,请写入
let filename = "test"
let contents = "Some Content"
do {
let directoryURL = try resolveURL(for: "testURL")
print(directoryURL)
let documentURL = directoryURL.appendingPathComponent (filename + ".txt")
try directoryURL.accessSecurityScopedResource(at: documentURL) { url in
try contents.write (to: url, atomically: false, encoding: .utf8)
}
} catch let error as ResolveError {
print("Resolve error:", error)
} catch {
print(error)
}
推荐阅读
- zsh - zsh 中的自定义组合命令
- reactjs - 自定义 View 或 ScrollView 组件
- bash - bash (macos) 函数在跟踪模式下工作,但在普通 shell 中不工作
- python - sklearn's yeo-johnson PowerTransformer throws "ValueError: Input contains infinity" when data has no large/inf/nan values
- html - 如何修复heroku应用程序上的损坏图像问题?
- azure-devops - Arm 部署模板输出未作为变量添加到 Devops 管道中
- node.js - 身份验证:在 JWT 中编码自定义声明
- algorithm - 压缩具有特定顺序的正整数 (int32) 向量
- javascript - 不推荐使用错误过滤器如何解决 vue.js 3 中的此错误
- asp.net - FileStream 对象永远将文件推送到浏览器