在云端 Mac 上验收 iOS 文档导入的权限与文件生命周期

在云端 Mac 上验收 iOS 文档导入的权限与文件生命周期

团队在云端 Mac 上调试 iOS 应用时,常见的误判是:从“文件”里选中 PDF,预览成功,就认为导入已经完成。实际可能只是应用暂时读到了外部文件;下次启动时,原路径失效,阅读记录便打不开。要验收的不是选择器能否弹出,而是文件从外部提供方进入应用后的权限、归属和生命周期。

本文以“导入 PDF 后长期阅读”为例。开发机可以远程连接,但文件访问规则仍由运行应用的 iOS 设备决定。先把导入实现和自动化测试分开:前者处理真实选择器交付的 URL,后者验证应用拥有的副本与失败分支。

先定义导入完成的边界

打开文件不等于拥有文件。使用 UIDocumentPickerViewController(forOpeningContentTypes: [.pdf], asCopy: false) 时,回调中的 URL 指向选择器提供的位置,不应直接存入阅读记录作为永久路径。业务若要求下次仍能打开,就要在访问有效期间读取原件,复制到应用自己的持久目录,并让记录指向新副本。

先约定三个可观察结果:取消选择不新增记录;复制失败不新增“成功”记录;成功后即使原件被移动,应用重启仍能打开自己的副本。临时目录适合承接一次导入操作,不适合充当长期书库。记录需要长期保存时,再把已验收的临时副本移入 Application Support 等应用持久目录。

判断导入成功的时间点应在副本写入并验证之后,而不是选择器返回 URL 的瞬间。

在有效访问期内完成协调复制

选择器回调拿到 URL 后,先开启安全作用域访问,再用 NSFileCoordinator 协调读取。下面函数仅演示单个、小型 PDF 的暂存步骤;调用方还须决定如何验证内容、迁入持久目录及更新记录。示例采用 asCopy: false 的打开模式,不适用于把所有来源都假定为外部授权文件的通用复制函数。

func stagePickedPDF(_ url: URL) throws -> URL {
    guard url.startAccessingSecurityScopedResource() else {
        throw CocoaError(.fileReadNoPermission)
    }
    defer { url.stopAccessingSecurityScopedResource() }

    let destination = FileManager.default.temporaryDirectory
        .appendingPathComponent(UUID().uuidString)
        .appendingPathExtension("pdf")
    var coordinationError: NSError?
    var copyError: Error?

    NSFileCoordinator().coordinate(
        readingItemAt: url,
        options: [],
        error: &coordinationError
    ) { readableURL in
        do {
            try FileManager.default.copyItem(
                at: readableURL,
                to: destination
            )
        } catch {
            copyError = error
        }
    }

    if let coordinationError { throw coordinationError }
    if let copyError { throw copyError }
    return destination
}

coordinate 是同步调用,不要让大文件复制长期占住界面线程。实际实现可把整个“开启访问—协调读取—停止访问”过程安排到后台任务;不要先在回调中停止访问,再把外部 URL 交给后台任务。错误路径同样必须停止访问,defer 在这里比散落的手动关闭更容易检查。

把暂存文件变成可恢复的记录

复制返回后,先检查副本存在、长度合理,并让 PDF 解析器尝试打开它;仅凭 .pdf 后缀不能证明内容正确。验证成功,再移动到应用持久目录,并以应用生成的标识保存相对路径。若验证或移动失败,清除暂存文件,向用户显示可重试的错误,不写入阅读列表。

持久化还要考虑“文件写好了、记录没写好”的中途失败。可以先写待提交记录,完成移动后再标记可用;启动时扫描未完成记录,核对对应文件并清理残留。具体事务方案取决于应用的数据存储方式,但目标一致:列表里不能出现指向不存在文件的条目。

不要把选择器提供的显示名称直接拼进目标路径。使用应用生成的文件名,把原名作为展示字段保存,既能避免重名覆盖,也能减少路径字符引起的意外。若产品要求始终读取外部原件,而非保留副本,那是另一种访问契约:需要单独设计重新授权及来源不可用时的界面,不能复用“已导入本地”的成功状态。

在云端 Mac 上分层验收

先用 XCTest 给导入后的逻辑喂应用沙盒内的 PDF 样本:检查解析成功、损坏文件报错、同名文件不会覆盖,以及删除暂存文件后列表是否仍指向持久副本。这层测试不应伪称自己验证了安全作用域授权;沙盒样本通常没有真实文件提供方的访问条件。

随后在模拟器上走完整界面:准备一份可由“文件”选择器看到的 PDF,分别执行选择与取消;导入后退出并重新启动应用,确认阅读记录可打开。再尝试移走选择器中的来源文件,观察应用是否确实在读取自己的副本。自动化无法稳定操控的提供方行为,记录为人工验收项,不要用等待固定秒数掩盖不确定性。

最后在目标设备及实际会使用的文件提供方上复核授权和下载行为。有些文档在选择时尚未完整存到本地;读取失败时,界面应保留错误并允许重新选择,而不是创建空白副本。这里验收的是错误处理,不是为提供方承诺下载时长。

用失败矩阵收尾

操作 应看到的结果
取消选择 阅读列表不变,没有残留暂存文件
来源无法读取 明确报错,不建立成功记录
副本验证失败 清理暂存文件,保留重试入口
导入后移动来源 重启后仍能打开应用副本
导入时中断应用 下次启动能识别并处理未完成记录

验收记录最好附上使用的文件类型、来源类别、系统与应用构建版本,以及每项操作的实际结果。这样在远程桌面上复现问题时,团队能先判断失败发生在授权、协调读取、复制、验证还是记录提交,而不是反复点击同一份文件。

常见问题

从文档选择器拿到 URL 后,可以直接保存路径供下次打开吗?

不要把外部 URL 的路径当作持久文件。若业务需要长期读取,应在有效访问期间协调读取,并复制到应用自己的持久目录;重启后从沙盒副本打开。

模拟器里的导入测试能否替代真实文件提供方测试?

不能。沙盒测试适合验证复制、解析和错误处理;发布前仍需通过实际文档选择器检查授权、取消操作,以及提供方中文件被移动或暂不可用的情况。

XCZone · 云端 Mac

需要一台可远程连接的独享物理 Mac?

核对 M4 Start、M4 Work 与 M4 Pro 的配置、节点和计费周期,再选择适合任务的方案。

查看云端 Mac 方案