📃 APFS - 苹果沙盒文件系统基础知识

2024/01/04

Sandbox 沙盒目录

每个iOS应用程序在读写文件时都有自己的沙盒目录。出于安全考虑,iOS 应用与文件系统的每次交互都限制在这个沙盒目录中。例外情况是对用户数据(如照片、音乐、联系人等)的访问请求。

沙盒结构目录如下:

Bundle 容器目录(Bundle Container Directory):应用程序的 bundle 资源(xxx.app),包含了应用程序中所有资源文件,比如图片、字符串文件、本地化资源等等,它仅有读取的权限。

数据容器目录(Data Container Directory):用于保存应用程序和用户数据。目录分为:

  • Documents - 用于存储用户生成的内容。
  • Library - 存储不应该暴露给用户的应用程序文件。
  • tmp - 临时文件,系统会定期清理这些文件。

Documents

文档目录,Apple 建议使用 Documents 存储用户生成的内容。这包括用户通过我们应用程序中产生(删除)的任何内容,比如文本文件、绘图、图片、视频文件等,可以添加子目录组织这些内容。

系统还会创建 Documents/Inbox目录,可以使用它来访问其它应用程序要求打开的文件,在这里可以读取/删除该目录文件,但不能编辑/新增文件。

Library

Library 目录包含了两个标准子目录,用于存储应用程序的支持文件。

  • Library/Application Support/ - 用于存储应用程序的需要,但不应该暴露给用户的任何文件,比如配件文件、模板、数据文件等。
  • Library/Caches - 用于缓存可以重新创建,并且需要比 tmp 目录的文件更持久的数据,系统可能在极少数情况下删除该目录以释放空间。

管理文件

访问 Bundle 资源:Foundation 框架提供了 Bundle 类型,可以直接定位资源。

// appConfig.json
let url = Bundle.main.url(forResource: "appConfig", withExtension: "json")

// 转换它
let data = try Data(contentsOf: url)
let configuration = try JSONDecoder().decode(ConfigurationModel.self, from: data)

数据容器目录中读写:为了管理 Documents 或 Library 目录中的文件,Foundation 框架提供了 FileManager 类型。

例如定位到 Library/Application Support 目录,可以执行以下操作:

let directoryURL = try FileManager.default.url(
    for: .applicationSupportDirectory,
    in: .userDomainMask,
    appropriateFor: nil,
    create: false
)

FileManager 参数中 for 枚举值:

  • .applicationSupportDirectory ==>"Library/Application Support"
  • .cachesDirectory ==> "Library/Caches"
  • .documentDirecotry ==> "Documents/"

🔥 另外,从 iOS16 开始,可以通过 URL 类型更加方便访问目录。

let url = URL.documentsDirectory // Documents/
let fileURL = URL.documentsDirecotry.appending(path: "myfile")

iOS16 后新增的 URL 常见静态属性有:

  • .applicationDirectory
  • .applicationSupportDirectory 🌟
  • .cachesDirectory 🌟
  • .documentsDirectory 🌟
  • .libraryDirectory 🌟
  • .temporaryDirectory 🌟
  • .downloadsDirectory
  • .homeDirectory
  • .desktopDirectory
  • .....

使用 FileManager 创建文件

let myDir = URL.applicationSupportDirectory.appending(path: "mydir")
try FileManager.default.createDirectory(
    at: myDir,
    withIntermediateDirectories: false,
    attributes: nil
)

注意如果目录已经存在,上面代码会抛出错误。在创建目录之前先用 FileManager.fileExists(atPath:) 进行检查,或者错误捕获。

文件备份

如果用户已经打开了iCloud备份,DocumentsLibrary/Application Support/ 目录中的所有文件都会默认备份。

Apple 建议从备份中排除所有可以通过服务器重新下载创建的文件。

var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
url.setResourceValues(resourceValues)

要从备份中排除一个URL,我们可以使用 URLResourceValues 类型,并将 isExcludedFromBackup设置为 true