You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 03:53:15