macos 应用沙盒按路径来源和用户授权控制文件访问,不识别文件类型;权限依赖 nsopenpanel/nssavepanel 显式选择、entitlement 声明特定系统目录、或容器内自有路径;security-scoped bookmark 需校验 staleness 并存于容器内。

macOS 应用沙盒对文件访问的控制不是按“文件类型”(如 .pdf、.mp4)划分的,而是严格按路径来源和用户授权方式决定权限。没有“允许读取所有图片”或“自动放行文档类文件”这类规则——系统不识别后缀,只校验访问路径是否合法、凭证是否有效、entitlement 是否匹配。
用户显式选择的文件/文件夹(最常用)
这是绝大多数第三方应用获取外部文件访问权的唯一合规途径:
- 必须通过
NSOpenPanel或NSSavePanel弹出系统原生对话框,不能自行构造路径字符串 - 若需访问整个文件夹(如 ~/Documents),需设置
canChooseDirectories = true,并启用resolvesAliases = true - 每次实际读写前,必须调用
url.startAccessingSecurityScopedResource();操作完成后立即调用url.stopAccessingSecurityScopedResource() - 如需重启后继续访问,须生成 security-scoped bookmark:
url.bookmarkData(options: .withSecurityScope),并存入自身容器目录(例如Application Support)
标准系统文件夹的声明式访问(有限且需明确配置)
部分系统目录支持通过 entitlement 直接声明访问权限,但仅限特定用途且不可绕过用户意图:
-
Downloads 文件夹:需勾选 Capabilities 中的 “Downloads Folder”,对应 entitlement
com.apple.security.files.downloads.read-write -
Pictures/Movies/Music 文件夹:分别对应
.pictures.read-write、.movies.read-write、.music.read-write - 这些权限仍受用户首次使用时的系统提示约束(Info.plist 需含对应 usage description),且仅开放该文件夹根目录层级,子目录不自动继承
- 不支持声明访问 ~/Documents 或 ~/Desktop —— 这两个位置必须走用户选择流程
应用自身容器内文件(无需额外授权)
沙盒应用天然拥有完全读写权限的区域,是安全存储首选:
- 主容器路径:
~/Library/Containers/<bundle-id>/Data/</bundle-id>,包含Documents、Caches、Preferences等子目录 - 临时目录:
~/Library/Containers/<bundle-id>/Data/tmp/</bundle-id>,适合短期缓存,系统可能自动清理 - 所有数据应默认存放于此;对外导出文件时,再通过
NSSavePanel让用户指定保存位置
书签恢复与过期处理(容易被忽略的关键环节)
security-scoped bookmark 并非永久有效,系统可能因路径变更、权限重置等原因使其失效:
- 调用
URL.resolvingBookmarkData(_:)时,必须传入&isStale参数并检查返回值 - 若
isStale == true,说明书签已失效,不能再直接 startAccessing,必须重新触发NSOpenPanel获取新授权 - 切勿将 bookmarkData 存在 tmp 目录或全局路径(如 /tmp),只能存于自身容器内,否则沙盒运行时无法读取
- 每次成功恢复 URL 后,仍需再次调用
startAccessingSecurityScopedResource()才能开始读写











