I have an NSFetchResultsController that provides data for a UITableView. I have an add button in the navigation bar that lets the user add a new entity in the next view.
In the next view I create a new entity and insert it into the context. The user can then save this change. However, if the user decides to not keep this new entity, they can hit cancel and I call context.rollback()
to get rid of the new entity. This causes my fetchresultscontroller to throw an exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
This is the error shown when I have two entities, and I create a third one and hit cancel.
The exception is thrown right as I perform the rollback, on the other view. I perform the insert with the following code:
let entity = NSEntityDescription.entityForName("template",
inManagedObjectContext: context!)
let newTemplate = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: context) as! Template
newWorkoutTemplate.setValue("New Template", forKey: "name")
self.template = newWTemplate
I set the undo manager of the context before performing this insert. Why does the NSFetchResultsController not detect the insertion of the new entity, but detects its deletion when I perform the rollback? Is it because both the insert and the deletion are performed on the same entity?
I realized I already had a didChangeObject
fetchResultControllerDelegate method implemented in my original VC, and this was the culprit. The line
templatesTable.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
was being called when the other VC called context.rollback()
So I solved this by adding a bool inAnother
to my first VC. Initially I set it to false. When I segue to the other view, I set it to true. Then, when the rollback happens in the other view, my fetchController's didChangeObject
is called. Within didChangeObject
, I check if I'm in the other view, and do not do anything if I am:
if (!inAnother && type == NSFetchedResultsChangeType.Delete) {
templatesTable.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
}
Also, I set inAnother
back to false in my first VC's viewWillAppear()
to make sure row deletions continue functioning properly.