0 Replies
      Latest reply on May 3, 2019 2:57 PM by Jaanus
      Jaanus Level 1 Level 1 (0 points)

        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?