4

I got a NSFetchedResultsController that I set up using a NSManagedObjectContext. I perform a fetch using this context.

I have as well a NSBatchUpdateRequest that I set up using the same NSManagedObjectContext. I execute the request using the same NSManagedObjectContext.

When I perform the request with the NSBatchUpdateRequest, I can see that all my data have been updated. If I restart the app, any fetch using NSFetchedResultsController is working as well.

THe problem is when I'm not restarting the app and that I do both operations one after one, I got a NSMergeConflict (0x17427a900) for NSManagedObject (0x1740d8d40) with objectID '0xd000000001b40000... error when I call the method save from my context.

I know that the problem comes from concurrent change on the same data but I don't know what is the solution? One might be to go through the NSMergePolicy class, but I doubt that's a clean way to solve my problem.

What should I do? Have two different contexts ? (how?)

Nico
  • 6,269
  • 9
  • 45
  • 85

1 Answers1

4

Well it seems I might have found how to do it, but if you see anything wrong, please let me know.

When you do a batch update, you have the possibility to get as a result, whether nothing, the number of rows that were updated or a list of object IDs that were updated. You have to choose the last one.

Once you perform executeRequest from the context, you need to get the list of object IDs, loop through all of them to get every NSManagedObject into Faults thanks to the method objectWithID of the context object. If you don't know what Faults object are in Core Data, here is the explanation.

With every NSManagedObject you get, you need to refresh the context using its method refreshObject.

Once you've done that, you need to perform again the performFetch of your fetchedResultsController to come back to where you were before the batch update.

Tell me if I'm wrong somewhere.

Here is the code:

let batchUpdate = NSBatchUpdateRequest(entityName: "myEntity")
batchUpdate.propertiesToUpdate = ["myPropertieToUpdate" : currency.amountToCompute]
batchUpdate.affectedStores = managedContext.persistentStoreCoordinator?.persistentStores

batchUpdate.resultType = .UpdatedObjectIDsResultType

var batchError: NSError?
let batchResult = managedContext.executeRequest(batchUpdate, error: &batchError) as NSBatchUpdateResult?
if let result = batchResult {
    println("Records updated \((result.result as [NSManagedObjectID]).count)")

    // Extract Object IDs
    let objectIDs = result.result as [NSManagedObjectID]

    for objectID in objectIDs {
        // Turn Managed Objects into Faults
        let nsManagedObject: NSManagedObject = managedContext.objectWithID(objectID)

        if let managedObject = nsManagedObject as NSManagedObject? {
            managedContext.refreshObject(managedObject, mergeChanges: false)
        }
    }

    // Perform Fetch
    var error: NSError? = nil
    if !fetchedResultsController.performFetch(&error) {
        println("error: + \(error?.localizedDescription), \(error!.userInfo)")
    }
} else {
    println("Could not update \(batchError), \(batchError!.userInfo)")
}

EDIT: Here are two links for more explanations:

http://code.tutsplus.com/tutorials/ios-8-core-data-and-batch-updates--cms-22164

http://www.bignerdranch.com/blog/new-in-core-data-and-ios-8-batch-updating/

Nico
  • 6,269
  • 9
  • 45
  • 85
  • The Faults like is broken. This may be the new location: [Faults](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html) – jk7 Jan 13 '16 at 00:52
  • Almost, you gave the `Mac` one. I updated with the iOS (which is probably the same). Thanks – Nico Jan 13 '16 at 02:33