Hi I\'m having a hard time fixing this error.
Terminating app due to uncaught exception \'NSInternalInconsistencyException\', reason: \'no object at i
Here's the helper method I wrote based on the accepted answer, but in Swift 3:
func validateIndexPath(_ indexPath: IndexPath) -> Bool {
if let sections = self.fetchedResultsController?.sections,
indexPath.section < sections.count {
if indexPath.row < sections[indexPath.section].numberOfObjects {
return true
}
}
return false
}
EDIT (USAGE)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier(), for: indexPath)
if self.validateIndexPath(indexPath) {
configureCell(cell: cell, indexPath: indexPath)
} else {
print("Attempting to configure a cell for an indexPath that is out of bounds: \(indexPath)")
}
return cell
}
The problem is inside your configureCell:atIndexPath method. Unfortunately the implementation that is provided in the Core Data Xcode templates has a few bugs. Your crash is one of them.
The NSFetchedResultsController method objectAtIndexPath: is not very safe to call. If you give it an NSIndexPath that is out of range it will crash with the very exception you are seeing. This is mentioned in the class reference:
If indexPath does not describe a valid index path in the fetch results, an exception is raised.
This is just like access to an array: whenever you access an array by an index, you should do a bounds check first. A bounds check for objectAtIndexPath: would look like:
id result = nil;
if ([[self.fetchedResultsController sections] count] > [indexPath section]){
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:[indexPath section]];
if ([sectionInfo numberOfObjects] > [indexPath row]){
result = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
}
And that's a bit complicated, but necessary. That solves the exception.
The reason you are getting an exception is that the state of the NSFetchedResultsController is getting out of sync with the state of the tableview. When your crash happens the tableView just asked your delegate methods (numberOfSectionsInTableView and tableView:numberOfRowsInSection: for the number of rows and sections. When it asked that, the NSFetchedResultsController had data in it, and gave positive values (1 section, 3 rows). But in-between that happening and your crash, the entities represented by the NSFetchedResultsController were deleted from the NSManagedObjectContext. Now cellForRowAtIndexPath: is being called with an outdated indexPath parameter.
It looks like egoRefreshScrollViewDataSourceDidFinishedLoading is causing the tableView to be rebuilt by adjusting the content view of the scroll view - and cellForRowAtIndexPath: to be called - before you refetch and reload your tableView. That needs to happen before whatever egoRefreshScrollViewDataSourceDidFinishedLoading is doing. egoRefreshScrollViewDataSourceDidFinishedLoading is causing cellForRowAtIndexPath: to be called with stale index paths.
The real problem may lie in your NSFetchedResultsControllerDelegate methods. When your entities are being deleted, those should be getting called to remove items from the tableView. That does not seem to be happening here.
The short version though, move your [self.tableView reloadData]; up one line:
[self.tableView reloadData];
[_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self.tableView];