在拿SwiftUI练手Mac客户端ZXKitLogger_Mac时,发现通过SwiftUI可以很简单的就为组件增加拖拽和文件处理。

为VStack添加文件拖拽

VStack(alignment: .trailing, spacing: 10) {
    List(self.fileList, id: \.path) { i in
        NavMenuItemView(url: i, selectedPath: $selectedPath)
            .onTapGesture {
                selectedPath = i.path
            }
    }
}.onDrop(of: ["public.file-url"], isTargeted: $dragOver) { providers in
    providers.first?.loadDataRepresentation(forTypeIdentifier: "public.file-url", completionHandler: { (data, error) in
        if let data = data, let path = String(data: data, encoding: String.Encoding.utf8), let url = URL(string: path) {
           //TODO: 获取并处理文件url
           //...
        }
    })
    return true
}

这样讲文件拖拽到VStack即可出现拖拽的标志。

文件的历史记录访问权限

通过上面的文件拖拽,可以得到文件的url进行文件处理。但是如果仅仅记录了该文件的url,退出了软件之后,如果不重复拖拽,直接访问该url进行操作会报错没有权限。

这是因为在项目中,默认的File Access就是用户选择的文件可读,没有选择直接通过url时权限不足,那么这样如果做文件列表的历史记录,只记录一个url地址明显不行。

这种情况就用到了Security-scoped bookmarks,Security-scoped bookmarks有2种模式可供选择:

1. An app-scoped bookmark

能为已沙盒程序提供对用户指定文件和文件夹的持久访问权。 例如,如果程序采用了一个程序容器外的下载或处理目标文件夹,通过NSOpenpanel对话框获得用户想使用该文件夹的初始访问权,包括拖拽等操作。

然后,为其创建一个 app-scoped bookmark,将它作为程序的配置储存(可能用属性列 表文件或 NSUserDefaults 类)。有了 app-scoped bookmark,程序就能获得对该文件夹的 未来访问权了。这种bookmark方式使用的比较多。

在.entitlements文件对应的key对应的权限,key为com.apple.security.files.bookmarks.app-scope,类型为Boolean,设置为true

2. A document-scoped bookmark

提供了对特定文档类型的的持久访问权。可以理解为针对文档类型的一种权限模式。比如你开发一个能编辑ppt文档的应用,里面嵌入了视频文件,图片文件连接。那么下次打开这个ppt文档时就能直接访问这些文件而不需要在通过NSOpenPanel打开获得授权。

Document-scoped bookmark只能指向文件而不是文件夹。并且这个文件必须不在系统使用的位置上(如/private 或/Library)

在.entitlements文件对应的key对应的权限,key为com.apple.security.files.bookmarks.document-scope,类型为Boolean,设置为true

bookmarks的使用

1、获取到URL的bookmarkData,可以存到存储到UserDefaults

if let book = try? url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) {
    UserDefaults.standard.set(book, forKey: ""UserDefaultsKey.fileListHistory.rawValue"")
}

2、通过bookmarkData 授权并读取该文件

var isStale = false
let url = try? URL(resolvingBookmarkData: data, options: [.withSecurityScope, .withoutUI], relativeTo: nil, bookmarkDataIsStale: &isStale)
if !isStale, url?.startAccessingSecurityScopedResource() == true {
    //授权未过期,可以直接读写该url
     return url
}

3、根据业务需要,也可以调用stopAccessingSecurityScopedResource()函数停止授权

url?. stopAccessingSecurityScopedResource()

参考文章


☟☟可点击下方广告支持一下☟☟

最后修改:2022 年 08 月 13 日
请我喝杯可乐,请随意打赏: ☞已打赏列表