Reputation: 656
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
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