Reputation: 6790
I'm having an issue getting accurate updates from an NSFetchedResultsControllerDelegate after using an NSBatchDeleteRequest, the changes come through as an update type and not the delete type on the didChange delegate method. Is there a way to get the changes to come in as a delete? (I have different scenarios for delete vs update).
The delete request: (uses a private context supplied by the caller)
fileprivate class func deletePeopleWith(ids: Set<Int>, usingContext context: NSManagedObjectContext) {
let fr: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Person")
var predicates: [NSPredicate] = []
ids.forEach {
predicates.append(NSPredicate(format: "id = %ld", $0))
}
fr.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
let dr = NSBatchDeleteRequest(fetchRequest: fr)
dr.affectedStores = context.parent?.persistentStoreCoordinator?.persistentStores
do {
try context.parent?.execute(dr)
context.parent?.refreshAllObjects()
print("Deleting person")
} catch let error as NSError {
let desc = "Could not delete person with batch delete request, with error: \(error.localizedDescription)"
debugPrint(desc)
}
}
Results in:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
DispatchQueue.main.async {
let changeDesc = "Have change with indexPath of: \(indexPath), new index path of: \(newIndexPath) for object: \(anObject)"
var changeType = ""
switch type {
case .delete:
changeType = "delete"
case .insert:
changeType = "insert"
case .move:
changeType = "move"
case .update:
changeType = "update"
}
print(changeDesc)
print(changeType)
}
}
Printing:
Have change with indexPath of: Optional([0, 0]), new index path of: Optional([0, 0]) for object: *my core data object*
update
Upvotes: 7
Views: 1941
Reputation: 11
Since NSBatchDeleteRequest
deletes the managed objects at the persistentStore level (on disk), the most effective way to update the current context (in-memory) is by calling refreshAllObjects()
on the context like so:
func batchDelete() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
let context = appDelegate.persistentContainer.viewContext
if let request = self.fetchedResultsController.fetchRequest as? NSFetchRequest<NSFetchRequestResult> {
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: request)
do {
try context.execute(batchDeleteRequest) // performs the deletion
appDelegate.saveContext()
context.refreshAllObjects() // updates the fetchedResultsController
} catch {
print("Batch deletion failed.")
}
}
}
}
No need for refetching all the objects or tableView.reloadData()
!
Upvotes: 1
Reputation: 13718
Here is the sample, works for me. But i didn't find how to update deletion with animation, cause NSFetchedResultsControllerDelegate
didn't fire.
@IBAction func didPressDelete(_ sender: UIBarButtonItem) {
let managedObjectContext = persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: Record.self))
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
do {
// delete on persistance store level
try managedObjectContext.execute(deleteRequest)
// reload the whole context
managedObjectContext.reset()
try self.fetchedResultsController.performFetch()
tableV.reloadData()
} catch {
print("🍓")
}
}
Upvotes: 2
Reputation: 2027
From Apple's documentation:
Batch deletes run faster than deleting the Core Data entities yourself in code because they operate in the persistent store itself, at the SQL level. As part of this difference, the changes enacted on the persistent store are not reflected in the objects that are currently in memory.
After a batch delete has been executed, remove any objects in memory that have been deleted from the persistent store.
See the Updating Your Application After Execution section for handling the objects deleted by NSBatchDeleteRequest
.
Upvotes: 13