Reputation: 6704
The premise: I have a UITableViewController
that conforms to NSFetchedResultsControllerDelegate
. I also have a fetched results controller and managed object context as variables in the controller. My tableView
displays a table with one section of core data objects from the fetched results controller.
What I'm trying to implement is swipe to delete. The object selected for deletion is actually deleted, however the wrong indexPath is being animated to delete and I don't know why. I currently have the following methods that I believe are relevant:
// This method is being called in viewDidLoad, adding all of the CoreData objects to an array called fetchedResults.
func performFetch() {
do { try fetchedResultsController?.performFetch()
fetchedResults = fetchedResultsController?.fetchedObjects as! [Date]
} catch let error as NSError {
print(error.localizedDescription)
}
}
// tableViewDataSource methods
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let objectToDelete = fetchedResults[indexPath.row]
fetchedResultsController?.managedObjectContext.deleteObject(objectToDelete)
print("commitEditingStyle-indexPath = \(indexPath)")
do { try managedContext.save()
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
// NSFetchedResultsControllerDelegate methods
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject object: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Delete:
if let indexPath = indexPath {
print("didChangeObject indexPath = \(indexPath)")
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
default:
return
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.tableView.endUpdates()
}
As you can see, I print the indexPath for the tableView:commitEditingStyle
method as well as the controller:didChangeObject
method. Here are the 2 print statements:
commitEditingStyle-indexPath = {length = 2, path = 0 - 3}
didChangeObject-indexPath = {length = 2, path = 0 - 0}
Why is the didChangeObject
method picking up the wrong indexPath? When I swipe to delete the object, the object is deleted at the proper indexPath (in this case 3...) but the table view cell that animates deletion is indexPath 0 (the first cell in my table view). What gives?
Upvotes: 1
Views: 209
Reputation: 119031
Remove all usage of fetchedResults
from your code. You are caching the initial set of objects that the FRC knows about, but you aren't tracking additions or removals in that cache. The cache is also a waste of memory because you can always get exactly what you want from the FRC and it also tracks changes.
So, what you're seeing should be what appears to be a random difference and is due to indexing differences between the cache array and the FRC. They should match initially, and if you only ever delete the last item it should be ok, but any other deletion would cause them to fall out of sync.
Upvotes: 0