离线、冲突与删除:同步真正困难的地方
两台设备都能离线修改,就可能出现互不知情的新版本。同步需要处理这些历史,而不仅是复制最新 UI。
用你的前端经验理解
可以借用 Git 的版本、共同祖先和合并思路;数据库记录不一定能按文本行合并,“最后写入获胜”也可能覆盖有价值的信息。
为什么需要它
同步最难的不是传输,而是多个副本各自合法修改后如何形成可解释结果。离线时设备没有完整全局信息,更新时间还可能受设备时钟影响。你需要按数据语义定义规则:标题需要保留用户意图,计数可能需要操作记录,删除则必须防止旧副本再次上传导致复活。
它是怎样工作的
可以把同步想成带身份的变更日志:每次变更有记录 ID、操作 ID 和版本依据,服务端或同步层检查是否基于旧版本。重复投递由幂等处理消化,冲突则选择字段合并、冲突副本或明确决胜规则。tombstone 保留删除事实,但何时安全清理取决于副本与同步协议,不是随意保留几秒。
把关键概念连起来
先区分状态
本地已保存、待上传、拉取中、冲突、失败与云不可用属于不同情况。重试要避免重复写入,可借助稳定操作/记录 ID 与可持久化的待处理状态。
冲突策略
显式 CloudKit 写入可根据服务端版本变化处理冲突;按字段合并、保留双方副本、用户选择都可能合理。SwiftData 自动镜像与自建 CKSyncEngine 的控制能力不同。
删除与恢复
离线设备可能再次带回旧内容。设计同步引擎时要处理删除变更/墓碑、变更令牌和账户切换;不能只拉当前记录而忽略删除历史。失败按错误类型退避重试。
“周末读”变成了两个标题
手机离线改成“周末读 Swift”,Mac 离线改成“周末读设计”。如果直接覆盖,就丢失一个意图;可以保留双方副本或让用户选择。框架具体怎样检测/合并,需要用真实工程验证。
推演删除与离线编辑相遇
- A 删除文章,B 离线修改同一文章标题。B 上线时不能简单把自己的整条记录当作新建上传。
- 合并层识别相同记录身份与删除事实,按产品规则保持删除,或明确提示恢复冲突内容。
- 相同变更重试多次后,最终结果仍应稳定,不能有时删除、有时重新出现。
这里需要你亲自判断
实验里的“手动选择版本”是帮助理解的冲突策略,不代表 SwiftData 默认会弹出冲突窗口,也不代表 CloudKit 自动采用相同规则。
做一个小练习
在同步实验中分别离线修改手机和 Mac 标题,再尝试同步并选择保留版本;解释覆盖与保留双方的代价。
- 在同步实验中制造双方改标题,再测试一方删除另一方编辑。
- 为“已读状态能否从 true 改回 false”定义规则,避免用逻辑 OR 意外禁止取消已读。
展开参考思路与验收标准
正确策略取决于业务意图。并非所有布尔值都可简单 OR,也并非最后时间戳就代表最后用户意图;规则应该能用一组固定冲突案例验证。