Introduction

This is the fourth part of the series CoreData: CRUD With Concurrency In Swift : DELETE.

If you didn’t read the first part, I would suggest you to read it since I introduced this series. You can find the second part here and the third one here.

In this article we are going to learn how to delete the data with CoreData using background queues—to avoid blocking the main queue.

CoreData provides mainly two ways to do it: Either using a NSManagedObject or NSBatchDeleteRequest .

Happy Reading!

Contents

Using NSManagedObject

Let’s consider that our App allows the user to delete a specific Dog . In this scenario, we would have a NSManagedObject object to delete.

Once we have a specific NSManagedObject , we can delete it very easily. Let’s see in the examples below:

iOS 8+

privateManagedObjectContext.perform { let dog: Dog = // Dog to delete do { // Deletes dog privateManagedObjectContext.delete(dog) // Saves in private context try privateManagedObjectContext.save() mainManagedObjectContext.performAndWait { do { // Saves the changes from the child to the main context to be applied properly try mainManagedObjectContext.save() } catch { fatalError("Failure to save context: \(error)") } } } catch { fatalError("Failure to save context: \(error)") } }

iOS 10+

persistentContainer.performBackgroundTask { privateManagedObjectContext in let dog: Dog = // Dog to delete do { // Deletes dog privateManagedObjectContext.delete(dog) // Saves in private context try privateManagedObjectContext.save() } catch { fatalError("Failure to save context: \(error)") } }

When we delete a NSManagedObject object, then we must save these changes manually—like in the examples above—to update our database.

Using NSBatchDeleteRequest

The approach of Using NSManagedObject has a problem. If we want to delete all the dogs in our system with a specific name, for example all the Max dogs, we should create a NSManagedObject per dog to delete and then delete all of them manually.

Fortunately, Apple introduced, in iOS 9, a better way to delete all the entries which satisfy the criteria of a specific predicate: NSBatchDeleteRequest .

It’s very similar to NSBatchUpdateRequest , which we have seen in the previous part of this series.

For the sake of explanation, let’s consider that we want to delete all the Dog s with the name Max . Let’s see how to do it using NSBatchDeleteRequest :

iOS 9+

privateManagedObjectContext.perform { // Creates a request for entity `Dog` let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog") // All the dogs with `name` equal to "Max" let predicate = NSPredicate(format: "name == %@", "Max") // Assigns the predicate to the request request.predicate = predicate // Creates new batch delete request with a specific request let deleteRequest = NSBatchDeleteRequest(fetchRequest: request) // Asks to return the objectIDs deleted deleteRequest.resultType = .resultTypeObjectIDs do { // Executes batch let result = try privateManagedObjectContext.execute(deleteRequest) as? NSBatchDeleteResult // Retrieves the IDs deleted guard let objectIDs = result?.result as? [NSManagedObjectID] else { return } // Iterates the object IDs objectIDs.forEach { objectID in // Retrieve a `Dog` object queue-safe let dog = mainManagedObjectContext.object(with: objectID) // Updates the main context mainManagedObjectContext.refresh(dog, mergeChanges: false) } } catch { fatalError("Failed to execute request: \(error)") } }

iOS 10+

persistentContainer.performBackgroundTask { privateManagedObjectContext in // Creates a request for entity `Dog` let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog") // All the dogs with `name` equal to "Max" let predicate = NSPredicate(format: "name == %@", "Max") // Assigns the predicate to the request request.predicate = predicate // Creates new batch delete request with a specific request let deleteRequest = NSBatchDeleteRequest(fetchRequest: request) // Asks to return the objectIDs deleted deleteRequest.resultType = .resultTypeObjectIDs do { // Executes batch let result = try privateManagedObjectContext.execute(deleteRequest) as? NSBatchDeleteResult // Retrieves the IDs deleted guard let objectIDs = result?.result as? [NSManagedObjectID] else { return } // Updates the main context let changes = [NSDeletedObjectsKey: objectIDs] NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [mainManagedObjectContext]) } catch { fatalError("Failed to execute request: \(error)") } }

As we saw in these examples, there are three main points to delete our data:

Create a request to filter the entity to delete. Execute the request with resultType set to resultTypeObjectIDs , since we need just the IDs of the objects deleted. Update the main context.

Note

If we want to support iOS 8, then we should fetch and delete all the entries manually like this:

privateManagedObjectContext.perform { // Creates a request for entity `Dog` let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog") // All the dogs with `name` equal to "Max" let predicate = NSPredicate(format: "name == %@", "Max") // Assigns the predicate to the request request.predicate = predicate // fetch returns just `objectID`s since we don't need of all the properties request.includesPropertyValues = false // Gets fetch result guard let dogs = try privateManagedObjectContext.fetch(request) as? [NSManagedObject] else { return } // Deletes dogs manually for dog in dogs { privateManagedObjectContext.delete(dog) } do { // Saves in private context try privateManagedObjectContext.save() mainManagedObjectContext.performAndWait { do { // Saves the changes from the child to the main context to be applied properly try mainManagedObjectContext.save() } catch { fatalError("Failure to save context: \(error)") } } } catch { fatalError("Failure to save context: \(error)") } }

We used the fetch property includesPropertyValues which lets return just the objectID s of the fetched elements. To delete an object we don’t need all its property, its objectID is enough.

Conclusion

We’ve just finished our journey in the CoreData concurrency world. Now, we are ready to ship faster Apps using CoreData in background queues. Concurrency is a scary world, fortunately CoreData is able to provide us an easy way to manage different queues without hurting ourself.

Share this: Facebook

LinkedIn

Twitter

Reddit

Pocket

More

Email

Print



