使用SwiftUI实现类Files/Finder文件树时多平台适配问题求助
问题解答
(a) isDirectory函数是否正确
当前实现逻辑本身没有错误,但存在更稳健低开销的写法:你可以直接通过URL的资源属性获取目录标记,不需要额外调用fileExists(atPath:)做文件存在性查询:
func isDirectory(_ url: URL) -> Bool { (try? url.resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true }
你遇到的目录识别错误问题大多不是函数本身导致,而是权限不足时fileExists返回false,间接让函数返回了错误结果。
(b) 隐藏文件判断的优化方案
只通过前缀.判断隐藏文件是不完整的,苹果平台还有大量通过NSURLIsHiddenKey属性标记的隐藏文件,这类文件不以.开头也会被系统判定为隐藏,优化后的实现如下:
func isHidden(url: URL) -> Bool { if url.lastPathComponent.starts(with: ".") { return true } return (try? url.resourceValues(forKeys: [.isHiddenKey]))?.isHidden == true }
(c) 假设iOS与MacOS目录结构相似是否错误
这个假设完全不成立:
- iOS采用严格的沙盒机制,每个应用只能访问自身沙盒内的指定目录,系统级的
/Users这类路径你根本没有访问权限,你拿到的返回只是系统返回的占位路径,不是真实可用的目录 - MacOS Catalyst应用默认也运行在沙盒中,就算是原生Mac应用如果没有获得对应权限,也不能随意读取其他用户目录的内容
- iOS的Files App能看到的文件分为三类:当前应用沙盒内的文件、其他应用开放给文件应用的文档、iCloud云盘文件,和MacOS的根目录结构完全无关
(d) 访问权限获取方案
你需要分平台配置对应权限:
- iOS端访问Files App可见文件:
- 在Info.plist中添加
UIFileSharingEnabled(应用支持iTunes文件共享)和LSSupportsOpeningDocumentsInPlace(支持在原地打开文档)两个键,值设为YES,这样你当前应用的Documents目录就会出现在Files App中 - 如果要访问其他应用的文件、或者用户主动选择的任意目录,需要使用
UIDocumentPickerViewController让用户主动选择目标文件/目录,获得安全范围书签之后即可持久访问对应路径
- 在Info.plist中添加
- MacOS/Catalyst端访问Finder可见文件:
- 如果你要访问下载、图片这类常见用户目录,可以在Xcode的Signing & Capabilities中添加App Sandbox能力,给对应目录开启读/写权限
- 如果需要全磁盘访问,需要在沙盒配置中开启Full Disk Access权限,或者直接放弃使用沙盒(注意放弃沙盒的应用无法上架App Store)
- 访问用户主动选择的任意目录,也可以通过
NSOpenPanel获取用户授权后读取内容
(e) Link组件无响应的问题
Link组件只能处理系统注册了对应URL Scheme的链接,file://协议的本地文件默认不会被Link处理,你需要替换为使用UIDocumentInteractionController(iOS/Catalyst)或者NSWorkspace.shared.open()(MacOS)来打开本地文件。
代码修复建议
你当前的初始化逻辑存在根本问题:FileManager.default.urls(for: .userDirectory, in: .allDomainsMask)在iOS端返回的并不是你有权限访问的目录,建议把默认查询的路径改成你应用自己的Documents目录:
init() { let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] _files = State(initialValue: getFiles(dir: docDir)) }
如果需要访问外部文件,必须先通过系统的文档选择器获取用户授权之后再读取。
内容的提问来源于stack exchange,提问作者Russ
相关产品推荐
相关产品推荐

