danilabagroff
danilabagroff

Reputation: 656

Who is calling delete?

I stack with this error:

CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to delete row 0 from section 0, but there are only 0 sections before the update with userInfo (null)

I have no idea who calls delete operation — honestly speaking I just don't have any deletes in the entire project. Any ideas how to debug such cases?

public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch(type) {
    case .insert:
        self.operation.append(BlockOperation(block: {
                                                        self.tableView?.insertRows(at: [newIndexPath!], with: .bottom)
                                                    })
        )
        // self.tableView?.scrollToRow(at: newIndexPath, at: .bottom, animated: true)
        break
    case .update:
        self.operation.append(BlockOperation(block: {
                                                        self.tableView?.reloadRows(at: [indexPath!], with: .bottom)
                                                    })
        )
        break
    default:
        break
    }
}

public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    self.tableView?.beginUpdates()
    for o in self.operation {
        o.start()
    }
    self.tableView?.endUpdates()

}

Thanks in advance.

Update 1

override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: ChannelTableCell = tableView.dequeueReusableCell(withIdentifier: "ChannelTableCell", for: indexPath) as! ChannelTableCell
    cell.setChannel(self.channelController.object(at: indexPath), delegate: self)

    return cell
}

Update 2

I have simplified didChange method and now get error right on reloadRows() call.

public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch(type) {
    case .insert:
        self.tableView?.insertRows(at: [newIndexPath!], with: .bottom)
        break;
    case .update:
        self.tableView?.reloadRows(at: [indexPath!], with: .bottom)
        break
    default:
        break
    }
}

Stack

*** First throw call stack:
(
    0   CoreFoundation                      0x0000000109db2d4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x00000001089cb21e objc_exception_throw + 48
    2   CoreFoundation                      0x0000000109db6e42 +[NSException raise:format:arguments:] + 98
    3   Foundation                          0x000000010856066d -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
    4   UIKit                               0x000000010ab1ab60 -[UITableView _endCellAnimationsWithContext:] + 6265
    5   UIKit                               0x000000010ab34f62 -[UITableView _updateRowsAtIndexPaths:updateAction:withRowAnimation:] + 331
    6   Joker                               0x00000001080335ac _TFC5Joker17AirViewController10controllerfTGCSo26NSFetchedResultsControllerPSo20NSFetchRequestResult__9didChangeP_2atGSqV10Foundation9IndexPath_3forOSC26NSFetchedResultsChangeType12newIndexPathGSqS4___T_ + 860
    7   Joker                               0x0000000108033740 _TToFC5Joker17AirViewController10controllerfTGCSo26NSFetchedResultsControllerPSo20NSFetchRequestResult__9didChangeP_2atGSqV10Foundation9IndexPath_3forOSC26NSFetchedResultsChangeType12newIndexPathGSqS4___T_ + 256
    8   CoreData                            0x0000000109a25400 __82-[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:]_block_invoke + 6608
    9   CoreData                            0x00000001098fa9a7 developerSubmittedBlockToNSManagedObjectContextPerform + 199
    10  CoreData                            0x00000001098fa85f -[NSManagedObjectContext performBlockAndWait:] + 255
    11  CoreData                            0x0000000109a23a17 -[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:] + 119
    12  CoreFoundation                      0x0000000109d505ec __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    13  CoreFoundation                      0x0000000109d504eb _CFXRegistrationPost + 427
    14  CoreFoundation                      0x0000000109d50252 ___CFXNotificationPost_block_invoke + 50
    15  CoreFoundation                      0x0000000109d13282 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 2018
    16  CoreFoundation                      0x0000000109d1231b _CFXNotificationPost + 667
    17  Foundation                          0x000000010849181b -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
    18  CoreData                            0x00000001098e2d90 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 704
    19  CoreData                            0x0000000109979a91 -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:deletions:updates:refreshes:deferrals:wasMerge:] + 1713
    20  CoreData                            0x00000001098dcb32 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 3554
    21  CoreData                            0x00000001098b5764 -[NSManagedObjectContext executeFetchRequest:error:] + 420
    22  libswiftCoreData.dylib              0x000000010c769e78 _TFE8CoreDataCSo22NSManagedObjectContext5fetchuRxSo20NSFetchRequestResultrfzGCSo14NSFetchRequestx_GSax_ + 56
    ...
)

Upvotes: 1

Views: 117

Answers (1)

Jon Rose
Jon Rose

Reputation: 8563

The immediate cause of your problem is that you are using indexPath for row update when you should use newIndexPath.

The reason is that indexPath is the index before any inserts or delete and newIndexPath is the index after the inserts and deletes.

If you want to update a tableView or collectionView from a fetchedResultsController and have it never crash this is how you do it: Have 5 mutable objects - rowInsert, rowDelete, rowUpdate, sectionInsert, sectionDelete. The rowInsert, rowDelete and rowUpdate are mutable arrays of indexPaths (ie NSMutableArray<NSIndexPath*>*). The sectionInsert and sectionDelete are mutable index sets (ie NSMutableIndexSet*). In controllerWillChangeContent clear the arrays. On each change add the appropriate indexPath. For insert and update use newIndexPath, for delete use indexPath, for move add newIndexPath to rowInsert and indexPath to rowDelete. In controllerDidChangeContent sort rowInsert and sectionInsert ascending and sort sectionDelete and rowDelete descending. (You can leave rowUpdate in whatever order it is in.) Now apply the changes in the following order: rowDelete, sectionDelete, sectionInsert, rowInsert, rowUpdate.

Upvotes: 1

Related Questions