CloudKit WWDC视频建议实现这样的同步:
我在我的应用中遵循这种模式,但我遇到了删除和父子关系的问题。
假设我们有一份分为类别的书籍清单。每本书都必须属于一个类别。
我从这样的数据开始:
SERVER
Thrillers: "Look Out!", "Secret Spy"
Non-Fiction: "Sailing the Seas", "Gardening Adventures"
Computer Programming: <empty>
如您所见,最终类别为空。假设我有两台具有此数据精确副本的设备。
现在,在设备1上,用户将书籍CloudKit Sync
添加到“计算机编程”:
DEVICE 1
Thrillers: "Look Out!", "Secret Spy"
Non-Fiction: "Sailing the Seas", "Gardening Adventures"
Computer Programming: "CloudKit Sync"
但是在设备2上,用户完全删除了“计算机编程”类别(它是空的,所以从设备2的角度来看这很好):
DEVICE 2
Thrillers: "Look Out!", "Secret Spy"
Non-Fiction: "Sailing the Seas", "Gardening Adventures"
设备1首先进行同步,因此会创建一个新的Book
条目,其parent
字段设置为Computer Programming
。
但现在设备2开始同步过程。它将更改应用于服务器,因此删除与“计算机编程”对应的CKRecord
。这与设备2的世界观一致,其中类别为空并且可以删除。
但是,当它从服务器中删除此类别时,这对于设备1和服务器本身的世界观来说没有意义。现在有一本名为CloudKit Sync
的孤儿书,它有一个指向其父母的悬挂指针。
如果我遵循Apple的WWDC建议,我该如何避免这种情况?根据同步的顺序,我很容易与孤立的书和无效的父级引用达到不一致的状态。
我想要发生的是来自设备2的Delete
命令返回错误,告诉我我要孤立一本书并阻止该操作发生,所以我可以采取一些采取行动解决问题。
这可能吗?还有另一种方法可以解决这个问题吗?
答案 0 :(得分:1)
是的,您可以使用设备2的行为。我看到cloudkit的三个方面将在您的场景中发挥作用。让我们先看一下,然后再看看它们在你的场景中的用法。
首先,假设两个(或所有)设备已订阅对相应记录的更改,将通知每个设备其他人添加或删除了某些内容。接收警报的设备将有机会决定如何处理它。 (从它的本地视图中删除它,在服务器上替换它等)
其次,您可以使用savePolicy
上的CKModifyRecordOperation
设置处理冲突的行为。您可以指定上次更改是否应覆盖旧记录,抛出错误等。有关这三个选项,请参阅https://developer.apple.com/documentation/cloudkit/ckrecordsavepolicy?language=objc。 (我只在两个用户修改公共记录的上下文中使用了这个,但是在另一个用户更新记录后删除应该抛出server record changed
错误。)
第三,假设您已配置上述savePolicy
,则服务器更改令牌本身。我发现最简单的想法是将更改令牌设想为最后修改的时间戳。 “我的这份记录的副本最后一次修改时间是晚上10:42”。根据您在前面提到的savePolicy
中选择的覆盖选项,设备将收到NSError Server Record Changed
,提醒您服务器上的版本来自,例如,晚上10:56,以及您当地的版本可能不再有效。
生成的NSError中的userInfo
包括3个版本的记录:服务器上的当前版本,您尝试提交的版本以及共同的祖先版本。 Apple的指南说,由开发人员决定如何合并这些信息。但从理论上讲,您可以区分变更,决定要保留哪些变量,然后提交新操作。
关于您的具体方案:假设您完全授权并信任dev1和dev2以删除记录,那么我将订阅创建和删除事件,并设置savePolicy
以在尝试冲突更改时抛出错误。在这种情况下,设备1将添加记录,设备2将接收新记录的通知。如果设备2只是尝试删除旧记录,它将失败并显示server record changed
错误,您可以将其显示给用户
“有人修改了这条记录,你真的想要删除它吗? (Y / N)。“
设备2必须在继续之前刷新记录(并接收新的记录更改令牌)。之后,如果设备2仍然想要删除新记录,则可以,但是然后设备1将通过上述订阅被通知该更改。然后,设备1将新记录下载到其本地视图(或在这种情况下从其中删除旧记录)。订阅通知可以提醒用户1:
“您的记录Foo刚被Bar删除”
即使事件几乎同时发生,这也会起作用,因为其中一个更改将首先应用于服务器,而另一个设备的令牌将立即变得过时。因此,如果设备2设法首先删除记录,则设备1尝试修改记录将失败并显示server record changed
,因为设备1的更改令牌现已过期。设备1的错误处理程序必须决定是否兑现删除或继续根据业务规则创建新记录。也许问一下用户1:
“计算机编程”已从服务器中删除。你想重新创作吗? 它?
此时,user1可以发送火焰电子邮件,要求其他用户停止删除新创建的记录,而user2可以要求人们停止重新创建他们刚刚“清理过”的记录。 :)
您可能会变得更复杂,可能使设备1优先于设备2,这样当设备1被通知记录被删除时,设备1会将记录重新写入服务器。如果您有多个具有删除权限的用户,则可以确定优先顺序并构建相应的错误/通知处理程序。然而,这似乎令人难以忍受的复杂和容易出错。可能会发生自动响应(创建,删除,创建,删除,创建,删除)的循环。我只把它作为一个假设的例子,而不是推荐!
最后,作为一个不同的示例,我的应用程序有不同的场景。我的案例中的记录是游戏会话。所有玩家都需要对会话数据的读访问权限,但只有发起人可以选择完全删除记录。因此,您可以考虑是否确实授权多个用户删除共享记录。