I am using a fairly standard setup of NSTableView + CoreData + NSFetchedResultsController, with the relevant view controller being NSFetchedResultsControllerDelegate to receive the changes. Here are the relevant bits of code from the view controller:
func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?){
print("Change type \(type) for indexPath \(String(describing: indexPath)), newIndexPath \(String(
switch type {
case .insert:
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath.item], withAnimation: .effectFade)
}
case .delete:
if let indexPath = indexPath {
tableView.removeRows(at: [indexPath.item], withAnimation: .effectFade)
}
case .update:
if let indexPath = indexPath {
let row = indexPath.item
for column in 0.. tableView.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: column))
}
}
case .move:
if let indexPath = indexPath, let newIndexPath = newIndexPath {
tableView.removeRows(at: [indexPath.item], withAnimation: .effectFade)
tableView.insertRows(at: [newIndexPath.item], withAnimation: .effectFade)
}
@unknown default:
fatalError("Unknown fetched results controller change result type")
}
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController) {
print("tableViewBeginUpdates")
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController) {
tableView.endUpdates()
print("tableViewEndUpdates")
}
I understand that I should be able to batch all the updates this way, even if multiple rows are deleted. However, this causes a crash with multiple deletes in a row.
Here is the log output from a session, with the table initially having five rows, and all of them being deleted:
tableViewBeginUpdates
Change type NSFetchedResultsChangeType for indexPath Optional([0, 4]), newIndexPath nil
Change type NSFetchedResultsChangeType for indexPath Optional([0, 3]), newIndexPath nil
Change type NSFetchedResultsChangeType for indexPath Optional([0, 1]), newIndexPath nil
Change type NSFetchedResultsChangeType for indexPath Optional([0, 0]), newIndexPath nil
Change type NSFetchedResultsChangeType for indexPath Optional([0, 2]), newIndexPath nil
The last row causes a crash:
2019-05-03 22:33:13.361102+0300 MyApp[2104:168334] [error] error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. NSTableView error inserting/removing/moving row 2 (numberOfRows: 1). with userInfo (null)
The first four deletes happen to be reported in the "right" order (rows with bigger indexes [row numbers] being deleted first). The last one arrives “out of order” and the other rows are seemingly already gone from NSTableView by this time.
Have I misunderstood how to work with the fetched results controller delegate? I thought that the beginupdates/endupdates calls make sure that the “table view model” does not change between them? What should I do to eliminate the crash?