Search code examples
swiftcloudkit

Swift/Cloudkit: Handling user-changes while pulling


I know from this StackOverflow question and from the Basic CloudKit Workflow that the prevalent way to sync with Cloudkit is to first push local changes and then pull remote changes so I can detect changes during the push by seeing it fail due to newer version on server.

But what if I do a longer pull

  • and the user makes a change and the user-change makes the data pulled invalid?
  • and the user makes changes which become obsolete due to the file already deleted?

One example would be the pull deletes a folder after the user put an edit to a file in said folder on the change on the to-be-uploaded stack. So the pull currently works while the user pushed the operation on the changes-list already since the change was done locally already.

One way I thought of to handle this could be to disallow any user changes while I pull from the server. Then I change the UI based on the pull and enable changes again.
But I do not know how long the push takes so UI unable to change things for some time may be bad user experience.

How to others handle(or avoid) pulls that invalidate user-made changes?


Solution

  • I am trying to avoid overridng a user-change done while I am pulling. Example: I have a file and the user saved changes to it while I pull down another version of the file.

    The local user-change is queued up already. So once I finish pulling and start pushing the user-local change I push a change that does not reflect the local cache anymore.

    I definitely get a version error when I try to push it, is it necessary to merge the change locally AND on the server Both?

    To avoid this issue I could wait queueing up the change, but name it a proposed change which I recognize during the pull and can act on. If the pulled change is incompatible with the proposed I may reject the proposed change and inform the user of the conflict.

    Note: This issue is separate from the CKError.Code.serverChangedError in that it is only relevant to changes made during pulls.

    TLDR: Pulls, pushes and local changes should not be allowed to happen at the same time.

    • Local changes during pushes are okay, they simply add to the next batch of changes to be pushed. I have to empty the push-local-changes batch and lock the local cache to proposed-mode before I push though, so the issue in question is avoided.
    • Local changes during pull become proposed changes, the validity of them has to be determined during merging of the pulled data or after and acted upon by informing the user.

    Linking the proposed change to the local cache object is necessary so the pull-merger can inform the proposed changed and store in it that the object changed and so the proposed changed may be invalid.

    An alternative to linking to proposed change is to have a version number in the object and on each change increase the version number(especially during changes from pull-mergers). On creation the proposed change holds the version number of the object it wants to change. After the pull-merge when the proposed changes are evaluated it can be evaluated by comparing the version number inside the object with the version number stored in the proposed change for the object.

    ———————————

    Another alternative is -after having pulled all the changes- to not merge them directly but push changes first until there are no local changes anymore, then block local changes and merge the pulled changes.

    I do not have to worry about pull-merge conflicts cause I adapted the user-change on the push already to the server changes, so the pull can’t invalidate the just-pushed user-made changes.

    ——————

    Alternative 3 is: Fetching all previously pulled until user made no changes during pull. Then locking the database shortly while merging the fetched records.

    Note: One can dicard a remote change of a record after one uploaded and fixed the merge conflict for said record.