I'm considering using CloudKit in my app (it doesn't use Core Data) and have read as many materials as I can find. I haven't fully grasped it yet and have a basic question on CKRecord.Reference. Does CloudKit guarantee CKRecord.Reference value is always valid? By valid I mean the target CkRecord pointed by the CKRecord.Reference exists in the database.
Let's consider an example. Suppose there are two tables: Account and Transaction:
Account Table:
AccountNumber Currency Rate
------------- -------- ----
a1 USD 0.03
Transaction Table:
TransactionNumber AccountNumber Amount
----------------- ------------- ------
t1 a1 20
Now suppose user does the following:
User first deletes account a1 and its associated transactions t1 on device A. The device saves the change to cloud.
Then user adds a new transaction t2 to account a1 on device B, before the device receives the change made in step 1 from cloud. Since a1 hasn't been deleted on device B, the operation should succeed locally. The device tries to save the change to cloud too.
My questions:
Q1) Will device B be able to save the change in step 2 to cloud?
I hope it would fail, because otherwise it would lead to inconsistent data. But I find the following in CKModifyRecordsOperation doc (emphasis mine), which implies CloudKit allows invalid reference:
During a save operation, CloudKit requires that the target record of the parent reference, if set, exists in the database or is part of the same operation; all other reference fields are exempt from this requirement.
(BTW, I think the fact that, when using CloudKit, Core Data requires all relations must be optional also indicates that CloudKit can't guarantee relation is always valid, though I think that is mainly an issue on client side caused by data transfer size. The above example, however, is different in that it's an issue on cloud side - the data on cloud is inconsistent).
I also find the following in the document. However, I don't think it helps in the above example, because IIUC CloudKit can only detect conflict when the changes on the same record but the changes in step 1 and step 2 are on different records.
Because records can change between the time you fetch them and the time you save them, the save policy determines whether new changes overwrite existing changes. By default, the operation reports an error when there’s a newer version on the server.
If the above understanding is correct, however, I don't understand why the same document has the following requirement, which implies CloudKit doesn't allow invalid reference:
When creating two new records that have a reference between them, use the same operation to save both records at the same time.
Q2) Suppose CloudKit allows invalid reference on cloud side (that is, device B successfully saves the change in step 2 to cloud) , I wonder what's the best practice to deal with it?
I think the issue is different from the optional relation requirement in Core Data when using CloudKit, because in that case the data is consistent on cloud side and eventually the client will receive complete data. In the above example, however, the data on cloud is inconsistent so the client has to remedy it somehow (although client has little information helping it).
One approach I think of is to avoid the issue in the first place. My idea is to maintain a counter in the database and requires client to increase the counter (it's not Lamport clock. BTW, is it possible to use Lamport clock in this case?) when making any change. This should help CloudKit to detect conflict (though I can't think out a good strategy on how client should deal with it. A simple one is perhaps to prompt user to select one copy). However, this approach effectively uses cloud as a centralized server, which I suspect isn't the typical way how people use CloudKit, and it requires clients to maintain local counter value in various situations. I wonder what's the typical approach? Am I missing something?
Thanks for any help.