Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSMergeConflict on iOS7

I have updated my app to support iOS 7 and have been faced with the problem that on one of screens in my [context save]; I get the following error:

NSCocoaErrorDomain Code=133020 "The operation couldn’t be completed. (Cocoa error 133020.)" UserInfo=0x1115a6d0 {conflictList=(
"NSMergeConflict (0x1115a670) for NSManagedObject (0xf25c850) with objectID '0xf25c070 <x-coredata://76AF57C8-F7FF-4880-B06B-63F8B780C96D/Screen/p7>' with oldVersion = 5 and newVersion = 6 
and old object snapshot = {\n    index = 3;\n    message = \"<null>\";\n    status = 0;\n} and new cached row = {\n    index = 3;\n    message = \"<null>\";\n    status = 0;\n}"

On iOS6 this problem does not occur.

Update: Code for managedObjectContext

-(NSManagedObjectContext *)managedObjectContextForCurrentThread{
if ([NSThread isMainThread])
{
    NSManagedObjectContext *parentContext = self.mainManagedObjectContext.parentContext;
    [parentContext performBlockAndWait:^{
        NSMergePolicy *mergePolicy = [[NSMergePolicy alloc] initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
        [[self mainManagedObjectContext] setMergePolicy:mergePolicy];
    }];
    return self.mainManagedObjectContext;
}
else
{
    NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
    NSManagedObjectContext *threadContext = [threadDict objectForKey:kCGMManagedObjectContextKey];
    if (threadContext == nil)
    {
        threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        NSManagedObjectContext *parentContext = self.mainManagedObjectContext.parentContext;
        [parentContext performBlockAndWait:^{
            NSMergePolicy *mergePolicy = [[NSMergePolicy alloc] initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
            [parentContext setMergePolicy:mergePolicy];
        }];
        [threadContext setParentContext:self.mainManagedObjectContext];
        [threadDict setObject:threadContext forKey:kCGMManagedObjectContextKey];
    }
    return threadContext;
}

}

like image 603
user2393695 Avatar asked Oct 04 '13 10:10

user2393695


4 Answers

I didn't want to mask a potential problem by setting a merge policy without understanding what was causing the NSMergeConflict first.

In my situation I had performed a NSBatchDeleteRequest earlier on in my code. The NSBatchDeleteRequest is performed directly on the persistent store coordinator so the ManagedObjectContext was unaware of the deletions and still held references to deleted objects. When I later referenced one of these objects and tried to save the context, the NSMergeConflict was thrown.

Calling reset() on my moc after performing the deletion fixed the issue.

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Tasks")
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
try managedContext.execute(batchDeleteRequest)
managedContext.reset() 
like image 152
Joseph Avatar answered Sep 22 '22 15:09

Joseph


I just spent two days debugging the exact same error. The difference between your app and mine is that mine only accesses core data from the main thread, so the merge error was even more puzzling.

In our case I narrowed it down to the fact that we had a unidirectional relationship - A has many Bs (modeled as an NSSet), but B doesn't know its A. We had a method that modified both an A and a B, and would cause a merge error when we went to save those changes. This code worked fine for a long time on both iOS 5 & 6, and only started to fail on iOS 7.

It's true that adding a merge policy will make the error go away, but it might also mask other errors. In our case we'd rather see those errors than risk having an inconsistent DB.

Changing the relationship to bidirectional made the error go away. The back links aren't necessary for our app, but they aren't hurting either. (And happily, changing this relationship was handled correctly as a lightweight migration - core data automatically filled in those back links for us.)

like image 27
sclamage Avatar answered Sep 21 '22 15:09

sclamage


Using Xcode 6.3.2 with Apple Watchkit extension and I got this same error when trying to make multiple updates and saves. The setMergePolicy solved the problem and here are the swift code:

context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

Make sure to put the above line before the context.save command.

like image 40
ffabri Avatar answered Sep 21 '22 15:09

ffabri


I got similar error, and in my case, locking NSPersistentStoreCoordinator worked.

[context.persistentStoreCoordinator lock];
[context performBlockAndWait:^{
   // do something
}];
[context.persistentStoreCoordinator unlock]

I don't know why it works, but I suspect NSManagedObjectContext's bug. I hope this will help.

like image 20
Takayuki Sato Avatar answered Sep 23 '22 15:09

Takayuki Sato