Implementing Batch Deletes

Follow the implementation guidelines in this chapter to avoid common pitfalls and produce maintainable code. You will learn how to:

Impacts of Using a Batch Delete

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.

Validation Rules

When you execute a batch delete, any validation rules that are part of the data model are not enforced. Therefore, ensure that any changes caused by the batch delete will continue to pass the validation rules. This naturally impacts validation rules on relationships.

Setting Up a Batch Delete

The goal of a batch delete is to delete one or more specific entities that are stored in a SQLite persistent store. To start a batch update, you create an NSBatchDeleteRequest that accepts an NSFetchRequest as part of its initialization.

let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Employee")
fetch.predicate = NSPredicate(format: "terminationDate < %@", NSDate())
let request = NSBatchDeleteRequest(fetchRequest: fetch)

When an NSBatchDeleteRequest is created it accepts an NSFetchRequest that describes which entities are to be deleted from the persistent store.

Executing a batch delete

After the NSBatchDeleteRequest is constructed, it is executed against an NSManagedObjectContext:

do {
    let result = try moc.executeRequest(request)
} catch {
    fatalError("Failed to execute request: \(error)")
}

The call to executeRequest() can throw an error and therefore requires the try keyword. If the call fails, the error can be reported. When the executeRequest completes successfully, a response is received. That response can take one of two forms. The form of the response is deteremined by setting the resultType property on the NSBatchDeleteRequest. The default value is NSStatusOnlyResultType, which returns nothing.

The other option is NSBatchDeleteObjectIDsResultType, which returns an array of NSManagedObjectID instances indicating which entities were deleted during the execution.

The resultType needs to be set prior to the execution of the NSBatchDeleteRequest. Regardless of the resultType that is set, the execution of the NSBatchDeleteRequest returns an NSPersistentStoreResult instance. If the resultType is set to NSBatchDeleteObjectIDsResultType, the value of the result property inside of the NSPersistentStoreResult instance is set.

Updating Your Application After Execution

If the entities that are being deleted are not loaded into memory, there is no need to update your application after the NSBatchDeleteRequest has been executed. However, if you are deleting objects in the persistence layer and those entities are also in memory, it is important that you notify the application that the objects in memory are stale and need to be refreshed.

To do this, first make sure the resultType of the NSBatchDeleteRequest is set to NSBatchDeleteRequestResultType.resultTypeObjectIDs before the request is executed. When the request has completed successfully, the resulting NSPersistentStoreResult instance that is returned will have an array of NSManagedObjectID instances referenced in the result property. That array of NSManagedObjectID instances can then be used to update one or more NSManagedObjectContext instances.

do {
    let result = try moc.execute(request) as? NSBatchDeleteResult
    let objectIDArray = result?.result as? [NSManagedObjectID]
    let changes = [NSDeletedObjectsKey : objectIDArray]
    NSManagedObjectContext.mergeChangesFromRemoteContextSave(changes, [moc])
} catch {
    fatalError("Failed to perform batch update: \(error)")
}

By calling mergeChangesFromRemoteContextSave, all of the NSManagedObjectContext instances that are referenced will be notified that the list of entities referenced with the NSManagedObjectID array have been deleted and that the objects in memory are stale. This causes the referenced NSManagedObjectContext instances to remove any objects in memory that are loaded which match the NSManagedObjectID instances in the array.