使用IceCream同步Realm与iCloud时,迁移后联网丢失本地数据问题
问题:Realm+iCloud同步迁移后phoneNumber属性被iCloud空值覆盖
问题背景
使用IceCream库实现Realm与iCloud同步,执行版本迁移时:
- 离线状态下,本地迁移可正常完成,将旧模型中
phoneNumbersList的号码转换为新模型的phoneNumber属性。 - 联网后,本地已迁移的记录会被iCloud端的旧记录(无
phoneNumber值)覆盖,导致phoneNumber数据丢失。
原有数据模型
@objc class Recipient: Object { @persisted(primaryKey: true) var rec_id = "" @persisted var firstName = "" @persisted var lastName = "" @persisted var phoneNumbersList = List<PhoneNumber>() @persisted var email = "" @persisted var isDeleted = false // IceCream requirement } @objc class PhoneNumber: Object { @persisted(primaryKey: true) var id = "" @persisted var number = "" @persisted var isDeleted = false // IceCream requirement }
修改后数据模型
@objc public class Recipient: Object { @persisted(primaryKey: true) var rec_id = "" @persisted var firstName = "" @persisted var lastName = "" @persisted var phoneNumber = "" @persisted var email = "" @persisted public var isDeleted = false // IceCream requirement }
解决方案
1. 迁移时同步更新iCloud端数据
在Realm迁移块中,完成本地数据转换的同时,确保更新后的Recipient对象被标记为需要同步到iCloud,让iCloud端的旧记录被新数据覆盖。
示例迁移代码:
let realmConfig = Realm.Configuration( schemaVersion: 2, migrationBlock: { migration, oldSchemaVersion in guard oldSchemaVersion < 2 else { return } migration.enumerateObjects(ofType: Recipient.className()) { oldObj, newObj in // 从旧的phoneNumbersList中提取号码(这里取第一个,可根据业务调整逻辑) if let phoneNumbers = oldObj?["phoneNumbersList"] as? List<DynamicObject>, let firstPhone = phoneNumbers.first, let number = firstPhone["number"] as? String { newObj?["phoneNumber"] = number } else { newObj?["phoneNumber"] = "" } // 触发对象修改标记,确保IceCream将更新同步到iCloud newObj?["isDeleted"] = newObj?["isDeleted"] } } ) // 应用配置 Realm.Configuration.defaultConfiguration = realmConfig
2. 自定义冲突解决策略
IceCream支持自定义冲突处理,当本地与iCloud的Recipient对象发生冲突时,优先保留本地已填充的phoneNumber值,避免被iCloud的空值覆盖。
示例自定义同步适配器:
class CustomCloudSyncAdapter: ICloudSyncAdapter { override func resolveConflict(for localObject: Object, with remoteObject: Object) -> Object? { guard let localRecipient = localObject as? Recipient, let remoteRecipient = remoteObject as? Recipient else { return super.resolveConflict(for: localObject, with: remoteObject) } // 本地有phoneNumber值,远程为空时,保留本地数据 if !localRecipient.phoneNumber.isEmpty && remoteRecipient.phoneNumber.isEmpty { return localRecipient } // 其他冲突场景沿用默认逻辑 return super.resolveConflict(for: localObject, with: remoteObject) } } // 替换默认适配器 ICloudSyncAdapter.shared = CustomCloudSyncAdapter()
3. 临时保留旧属性(可选)
暂时不要移除phoneNumbersList属性,将其标记为非持久化或保留为临时字段。等所有用户设备完成同步,iCloud端的Recipient记录都已更新为带有phoneNumber的版本后,再彻底移除该属性,避免同步过程中无法回退提取数据。
内容的提问来源于stack exchange,提问作者Muhammad Ibrahim
相关产品推荐
相关产品推荐

