How to Use NSBatchDeleteRequest in iOS 9

NSBatchDeleteRequest

In iOS 9, Apple introduced NSBatchDeleteRequest. This provides Core Data with a method for deleting large amounts of data without having to load the objects into memory. Today we’re going to look at the difference in using NSBatchDeleteRequest and the method for deleting objects in iOS 8 and earlier.

Setting Up You Project

To get started, download the BatchDelete project. Take a look around the project. NSBatchDeleteRequest is new for iOS 9, so that is the target for the project. The UI has three buttons and a label. These are wired up to ViewController.swift. Great! Let’s add our entity. Go to the BatchDelete.xcdatamodeld and add an entity named Comment. Add two attributes, comment and id. Your entity should look like this.

Comment Entity Core Data

Now you need to create an NSManagedObject Subclass. To do this, go to Editor and click ‘Create NSManagedObject Subclass…’.

Add NSManagedObject Subclass

Adding Properties

After the IBOutlets, add some properties to your ViewController.swift.

  1. We need an instance of NSPersistentStoreCoordinator. The is a lazy property, meaning it is not initialized until it is used. The Core Data stack is setup in the App Delegate by default. Here we grab the same instance that is created in the App Delegate.
  2. We’re doing the same thing here for the NSManagedObjectContext that we did in the previous step.
  3. Some of the actions we’re going to do take longer than a second so an activity indicator is going to be used. This will give us a visual indicator that something is happening.

Updating the UI

Whenever the buttons are pressed, we need to update our UI. Let’s add that method.

  1. As discussed in a previous post, a fetch request is required for getting data. The NSManagedContext  has a method countForFetchRequest error:  that takes the NSFetchRequest  and returns the number of objects. After this is done, we update our label and stop the activity indicator.
  2. Based on the count returned, we update our buttons. This prevents any action be duplicated.

At the bottom of viewDidLoad, add the activity indicator to the view and call your new method.

Adding Objects

Now we’re going to add some objects. Update addToRecords with the following code.

There’s a lot going on here so we’re going to break it down into much smaller chunks.

  1. Start the activity indicator, this is going to take a while. Disable the button, we don’t want to hit it more than once.
  2. Create another context. Why, because this is a lot of data and we need to do it on a background thread. Our current context is setup to call on the main thread. We are going to use the same NSPersistentStoreCoordinator.
  3. Get the current date and time, we’ll use this in a minute.
  4. NSManagedObjectContext  comes with performBlock. This will all be done in the background. First we need a reference to self, we want it to be weak to prevent any retain cycles. In the block we create 1,000,000 objects and load them to the data store. We assign a number to id and a string to the comment. This is not recommended for production, it takes a lot of memory and time to complete this.
  5. Once the objects are created, save them. The save method throws, so we have to wrap it in a do-try-catch.
  6. When it is all done, we need to update the UI. Since this work is all done on a background thread, we need to dispatch back to the main thread so we can update the UI. We also print out the time taken to create the objects.
  7. Print out the error, if there is one.

Don’t run your project just yet.

Deleting Objects

Let’s add a way to delete them first. Add the following code to the deleteOldWayRecords method.

So we’re going to look at this one even closer.

  1. Start the activity indicator.
  2. Set up our new context.
  3. Disable both delete buttons. No extra delete actions here.
  4. Create our fetch request. We only wan’t the NSManagedObjectID , so we set the includesPropertyValues  to false.
  5. Get the current time.
  6. Create our performBlock and get a weak reference to self.
  7. Execute our fetch request and save the results as an Array of NSManagedObject. This method throws so it uses the keyword try and is wrapped in do-catch.
  8. Iterate through the Array, deleting each object. Yep, that’s how it has to be done.
  9. Save the changes.
  10. Again, since we’re on a background thread we have to dispatch back to the main thread to update the UI and print our time statement.
  11. Catch any errors and print them to the console.

Whew! Now we can run the code and see what happens. That’s a long time to delete all those records. Your time will be a little different than mine, but here’s what I got. You can also look at the memory usage for deleting the objects. Mine got to about 500 mb.

Time to insert: 27.6551970243454

Time to delete old way: 40.9995949864388

There has to be a better way. Thanks to NSBatchDeleteRequest , we now have a better solution. Add the following to deleteNewWayRecords .

There’s a lot less code here, is this right? That’s what you might be thinking and yes it is right.

  1. Start the activity indicator and get the current time.
  2. Create our fetch request for our Comment entity.
  3. Create our delete request. NSBatchDeleteRequest fetchRequest:  requires a NSFetchRequest  as an argument.
  4. Now we’re going to use our current NSManagedObjectContext and execute our delete request. This method throws, so it has the try keyword and is wrapped in the do-try-catch. Then we save our context.
  5. Catch any errors that may be thrown and print them to the the console.
  6. Print the duration of the delete request and update the UI.

Run the project again.

Time to insert: 28.6179929971695

Time to delete old way: 42.5472519993782

Time to insert: 28.9774550199509

Time to delete with batch request: 0.35631799697876

Wow! Those are some huge differences. Only 0.356 seconds to delete using NSBatchDeleteRequest. We deleted all the objects. You could add a predicate to the fetch request and only delete certain objects. Try this out on your own. Only delete Comments whose id is less then 500,000.

4 thoughts on “How to Use NSBatchDeleteRequest in iOS 9

  1. NSBatchDeleteRequest is amazing invention, but now I’m working with iCloud sync and I found that when I use NSBatchDeleteRequest iCloud don’t want to update state. Really confusing…

    And what’s about you? Did you notice something similar? Maybe I’m doing it wrong.

  2. Did you try it with NSFetchedResultsController cause it doesn’t seems to work for me at all: delegate methods not fired and nothing happened

Leave a Reply

Your email address will not be published. Required fields are marked *