Discover PhotoKit change history
System & Services 进阶 20m

了解 PhotoKit 变更历史

Discover PhotoKit change history

2022年6月6日

在 Apple 官方观看视频

一句话判断

PhotoKit 新增的 Persistent Change History API 让你的 App 离线后也能精确知道照片库发生了什么变化——新增、修改、删除,一个 API 搞定。

这场 Session 讲了什么

之前要追踪照片库变化非常麻烦。新增资产需要按日期查询,修改需要比对 modification date(但不精确),删除更是要全量 diff。三种操作需要三种不同的检测方式,性能开销大,结果不可靠。

Persistent Change History 解决了这个问题。它提供一个 change token 代表照片库在某个时间点的状态。下次 App 启动时,用这个 token 查询之后发生的所有变更——精确到具体资产的增删改。token 可以跨 App 启动持久化保存。

这个 API 在所有支持 PhotoKit 的平台上都可用:macOS、iOS、iPadOS、tvOS。

Session 用一个登山照片拼贴 App 的案例完整演示了如何使用 change history 追踪照片新增(新的登山照片)、修改(添加滤镜后的重新渲染)和删除(拼贴需要重新生成)。

值得深挖的点

  • Change Token 的生命周期:Token 是设备本地存储的,获取成本极低。每次处理完变更后保存最新的 token,下次用这个 token 获取增量变更。
  • 与 PHChange 的互补关系:PHChange 处理 App 运行期间的实时变更。Persistent Change History 处理 App 不活跃期间的离线变更。两者可以同时使用,覆盖所有场景。
  • 变更详情的三种类型:资产(Asset)、资产集合(Asset Collection)、集合列表(Collection List)各有独立的变更详情。每次变更都包含 insertedIdentifiers、updatedIdentifiers 和 deletedIdentifiers。
  • hasAdjustments API:新的便捷方法检查资产是否被编辑过。如果资产有调整,你的 App 可能需要重新渲染缩略图或拼贴。

代码片段

import Photos

// 使用上次保存的 token 获取增量变更
let lastToken = loadSavedToken()  // 从 UserDefaults 或文件读取
let changes = PHPersistentChangeHistory.changeHistory(
    since: lastToken
)

// 遍历变更
var insertedAssetIDs: Set<String> = []
var updatedAssetIDs: Set<String> = []
var deletedAssetIDs: Set<String> = []

for change in changes {
    guard let details = change.changeDetails(for: .asset) else { continue }
    insertedAssetIDs.formUnion(details.insertedIdentifiers)
    updatedAssetIDs.formUnion(details.updatedIdentifiers)
    deletedAssetIDs.formUnion(details.deletedIdentifiers)
}

// 处理新增:检查是否是登山照片
let newAssets = PHAsset.fetchAssets(
    withLocalIdentifiers: Array(insertedAssetIDs),
    options: nil
)
// 按日期匹配登山活动...

// 处理修改:检查是否需要重新渲染
let updatedAssets = PHAsset.fetchAssets(
    withLocalIdentifiers: Array(updatedAssetIDs),
    options: nil
)
for i in 0..<updatedAssets.count {
    let asset = updatedAssets[i]
    if asset.hasAdjustments {
        // 重新渲染此资产的缩略图
    }
}

// 处理删除:重新生成受影响的拼贴
regenerateCollages(affectedBy: deletedAssetIDs)

// 保存最新的 token
saveLatestToken(changes.lastToken)

最佳实践

  • 在后台线程处理变更历史,照片库的变更可能非常多
  • 一次大批量请求比多次小请求更高效
  • 只关注你的 App 需要的变更类型,不要处理无关数据
  • 处理 token 过期错误:如果 token 太老超出历史范围,需要全量重新同步
  • 处理变更详情不可用的错误,回退到全量获取

还有什么值得关注

  • “What’s new in the Photos picker” 介绍了更简单的照片选择方案
  • PhotoKit 现在支持 Cinema 视频的媒体子类型和智能相册访问
  • 新增了网络错误码和文件同步错误码,帮助诊断资源加载问题
  • Limited Library 模式下只返回用户选择的 PhotoKit 对象的变更
WWDC 2022