Search code examples
iosxcodecore-datacloudkitnspersistentcloudkitcontainer

Is it necessary to use merge policy with CoreData and Allows external storage to avoid crash?


I use CoreData + NSPersistentCloudKitContainer to backup my model. One of my NSManagedObject has a picture stored as Binary Data and Allows external storage checked.

It's very random but sometimes when i call context.save() i get a merge conflict even if i don't have edit this object. In fact i just try to create a new object (different type as the one with conflicts) and the crash is on another object.

Everyting is the same value except the self reference of External Data Reference:

    ERROR AppDelegate.saveContext():262 - Unresolved error Error Domain=NSCocoaErrorDomain Code=133020 "Impossible de fusionner les changements." UserInfo={conflictList=(
    "NSMergeConflict (0x600002c62f40) for NSManagedObject (0x60000158da40) with objectID '0x816659a4c358c32f <x-coredata://CEAE3B14-5782-4C18-AAE8-B05D91CFEC8A/Aquarium/p5>' with oldVersion = 56 and newVersion = 57 and old object snapshot = {\n    albums = \"<null>\";\n    aquaID = 6;\n    dateDeCreation = \"2022-01-15 18:24:00 +0000\";\n    imageData = External Data Reference: <self = 0x60000158db80 ; path = 1B9355A6-4101-479D-81F1-DFC106BBCD75 ; length = 1617016>;\n    nom = 11321;\n    volume = 60;\n} and new cached row = {\n    albums = \"<null>\";\n    aquaID = 6;\n    dateDeCreation = \"2022-01-15 18:24:00 +0000\";\n    imageData = External Data Reference: <self = 0x6000015ccf50 ; path = 1B9355A6-4101-479D-81F1-DFC106BBCD75 ; length = 1617016>;\n    nom = 11321;\n    volume = 60;\n}"
), NSExceptionOmitCallstacks=true}, ["NSExceptionOmitCallstacks": 1, "conflictList": <__NSArrayM 0x600003a25ec0>(
NSMergeConflict (0x600002c62f40) for NSManagedObject (0x60000158da40) with objectID '0x816659a4c358c32f <x-coredata://CEAE3B14-5782-4C18-AAE8-B05D91CFEC8A/Aquarium/p5>' with oldVersion = 56 and newVersion = 57 and old object snapshot = {
    albums = "<null>";
    aquaID = 6;
    dateDeCreation = "2022-01-15 18:24:00 +0000";
    imageData = External Data Reference: <self = 0x60000158db80 ; path = 1B9355A6-4101-479D-81F1-DFC106BBCD75 ; length = 1617016>;
    nom = 11321;
    volume = 60;
} and new cached row = {
    albums = "<null>";
    aquaID = 6;
    dateDeCreation = "2022-01-15 18:24:00 +0000";
    imageData = External Data Reference: <self = 0x6000015ccf50 ; path = 1B9355A6-4101-479D-81F1-DFC106BBCD75 ; length = 1617016>;
    nom = 11321;
    volume = 60;
}

I suspect the data reference changed between the time the application loads and the first time I call the backup. But the length is the same since this value has not been changed on the user side, so I don't understand why the reference changes.

I use no merge policy right now and it just don't save.

Can i use NSMergeByPropertyObjectTrumpMergePolicy to be safe or should i find why the reference changed ?


Solution

  • I have never worked with CoreData external data references, so this is a wild guess.
    It seems to me that the merge conflict arises because the reference to the external file changed, although the path to the file stayed the same.
    This is probably a case for a custom merge policy. You had to resolve the merge conflict by your own code that simply selects one of the references because both point anyway to the same file.
    I tried recently to set up my own custom merge policy for a different situation, but I am not completely sure that I did it right, please see my question, related code and references here. Maybe you can test a similar code for your own case.

    EDIT
    Regarding your question, if you can use NSMergeByPropertyObjectTrumpMergePolicy: According to the merge conflict, all properties of your entity stayed the same except the imageData reference. So, before you try to use a custom merge policy, you should definitively try the standard merge policies:
    NSMergeByPropertyObjectTrumpMergePolicy updates only the imageData reference from the managed context, while NSMergeByPropertyStoreTrumpMergePolicy updates only the imageData reference from the persistent store. But since all other properties did not change, and the file path in both entities is the same, you could equally well try NSOverwriteMergePolicy and NSRollbackMergePolicy that use the entire entity in the managed context or the persistent store, respectively, see the docs. Only if that fails because of the external reference, you should consider a custom merge policy.