Search code examples
iosswiftxcodecore-datacloudkit

Swift • CloudKit • What is the difference between the various NSMergePolicy Objects?


I've implemented CloudKit into my iOS App using the NSPersistentCloudKitContainer.

While doing this, I came across the NSMergePolicy.

I searched for an explanation for the different options, however, the Apple Developer Documentation wasn't very helpful.

  • NSErrorMergePolicy
  • NSMergeByPropertyStoreTrumpMergePolicy
  • NSMergeByPropertyObjectTrumpMergePolicy
  • NSOverwriteMergePolicy
  • NSRollbackMergePolicy

Question: Can anyone explain, what the difference between these Policies is (if possible with examples)? And should I use the default on (NSErrorMergePolicy) or change it?


Solution

  • Merge policy matters when you're trying to save changes but there are other changes that were saved somewhere else-- like another thread, or CloudKit importing some data. For example,

    • You fetch an object
    • You make changes to the object but haven't saved changes yet
    • A different thread changes the same object, with different changes than the ones you just made
    • You try to save changes

    How does Core Data combine your unsaved changes with the new changes in the persistent store? It depends on your merge policy. By default, the merge policy is NSErrorMergePolicy, which means that you can't save changes because of the conflict. You have other choices, though.

    Apple's descriptions are short and to the point, so an example would probably help with understanding them. Suppose you have a Person entity and the properties are name, address, phone, and birthday. You fetched one instance and you're editing it, and another thread saves changes that conflict with your edits. It might look like this:

    Property Original Your change Other thread change
    name Taylor Swift Taylor Jones Taylor Swift
    address 123 Main St 123 Main St 123 Sesame St
    phone (212) 555-1212 1-800-275-2273 (650) 253-0000
    birthday Electronica Electronica Electronica

    Now you try to save changes, so what happens? How does Core Data resolve your changes vs. the other thread's changes? The built-in merge policies provide some options.

    Two of them merge "by property", which means Core Data looks at each property value and chooses one value or the other. NSMergeByPropertyStoreTrumpMergePolicy means that when the values are different, the value in the persistent store wins. NSMergeByPropertyObjectTrumpMergePolicy means the value in memory wins. [In these policy names, "trump" means outranking or winning, so it might be easier to think of them that way. Imagine that they're named NSMergeByPropertyStoreWinsMergePolicy and NSMergeByPropertyObjectWinsMergePolicy.] These only change values when there's a conflicting change. In the table above, that happens for phone.

    This is what happens when saving with those policies:

    With NSMergeByPropertyStoreTrumpMergePolicy, the persistent store wins when there's a conflict.

    Property Resolved value Explanation
    name Taylor Jones No conflict, your change is used
    address 123 Sesame St No conflict, store change is used
    phone (650) 253-0000 Conflict! Merge policy says the store wins
    birthday Electronica No changes

    With NSMergeByPropertyObjectTrumpMergePolicy, in-memory changes win the conflict:

    Property Resolved value Explanation
    name Taylor Jones No conflict, your change is used
    address 123 Sesame St No conflict, store change is used
    phone 1-800-275-2273 Conflict! Merge policy says the your change wins
    birthday Electronica No changes

    The others-- NSOverwriteMergePolicy and NSRollbackMergePolicy-- also mean that one version or the other wins. But for these two the policy can make changes even if conflicts don't exist. One or the other wins completely. In the table above, there are non-conflicting changes for name and address because only one version changes the original value, and of course there's a conflicting change for phone again.

    Here's what happens with those merge policies:

    With NSOverwriteMergePolicy, your in-memory changes win out everywhere, even if there's no conflicting change.

    Property Resolved value Explanation
    name Taylor Jones No conflict, your change is used
    address 123 Main St No conflict, but merge policy says your change wins
    phone 1-800-275-2273 Conflict! Merge policy says your change wins
    birthday Electronica No changes

    With NSRollbackMergePolicy it's the opposite: the persistent store wins everywhere, even if there's no conflicting change.

    Property Resolved value Explanation
    name Taylor Swift No conflict but merge policy says the store wins
    address 123 Sesame St No conflict, persistent store value is used
    phone (650) 253-0000 Conflict! Merge policy says the persistent store wins
    birthday Electronica No changes

    If none of these do what you want, you can subclass NSMergePolicy and write your own policy, by implementing resolve(mergeConflicts: [Any]) and maybe some other functions.