Core Data and Swift: Batch Updates

Even though Core Data has been around for many years on OS X and iOS, a feature that was added only recently are batch updates. Developers have been asking for this feature for many years and Apple finally found a way to integrate it into Core Data. In this tutorial, I will show you how batch updates work and what they mean for the Core Data framework.

1. The Problem

Core Data is great at managing object graphs. Even complex object graphs with many entities and relationships aren't much of a problem for Core Data. However, Core Data does have a few weak spots, updating large numbers of records being one of them.

The problem is easy to understand. Whenever you update a record, Core Data loads the record into memory, updates the record, and saves the changes to the persistent store, a SQLite database for example.

If Core Data needs to update a large number of records, it needs to load every record into memory, update the record, and send the changes to the persistent store. If the number of records is too large, iOS will simply bail out due to a lack of resources. Even though a device running OS X may have the resources to execute the request, it will be slow and consume a lot of memory.

An alternative approach is to update the records in batches, but that too takes a lot of time and resources. On iOS 7, it's the only option iOS developers have. That's no longer the case since iOS 8 and OS X Yosemite.

2. The Solution

On iOS 8 and up and OS X Yosemite and up, it's possible to talk directly to the persistent store and tell it what you'd like to change. This generally involves updating an attribute. Apple calls this feature batch updates.

There are a number of pitfalls to watch out for though. Core Data does a lot of things for you and you may not even realize it until you use batch updates. Validation is a good example. Because Core Data performs batch updates directly on the persistent store, such as a SQLite database, Core Data isn't able to perform any validation on the data you write to the persistent store. This means that you are in charge of making sure you don't add invalid data to the persistent store.

When would you use batch updates? Apple recommends to only use this feature if the traditional approach is too resource or time intensive. If you need to mark hundreds or thousands of email messages as read, then batch updates is the best solution on iOS 8 and up and OS X Yosemite and up.

3. How Does It Work?

To illustrate how batch updates work, I suggest we revisit Done, a simple Core Data application that manages a to-do list. We'll add a button to the navigation bar that marks every item in the list as done.

Step 1: Projet Setup

Download or clone the project from GitHub and open it in Xcode 7. Run the application in the simulator and add a few to-do items.

Adding To-Do Items

Step 2: Create Bar Button Item

Open ViewController.swift and declare a property, checkAllButton, of type UIBarButtonItem at the top.

Initialize the bar button item in the viewDidLoad() method of the ViewController class and set it as the left bar button item of the navigation item.

Step 3: Implement checkAll(_:) Method

The checkAll(_:) method is fairly easy, but there are a few caveats to watch out for. Take a look at its implementation below.

Create Batch Request

We start by creating an NSEntityDescription instance for the Item entity and use it to initialize an NSBatchUpdateRequest object. The NSBatchUpdateRequest class is a subclass of NSPersistentStoreRequest.

We set the result type of the batch update request to .UpdatedObjectIDsResultType, a member value of the NSBatchUpdateRequestResultType enum. This means that the result of the batch update request will be an array containing the object IDs, instances of the NSManagedObjectID class, of the records that were changed by the batch update request.

We also populate the propertiesToUpdate property of the batch update request. For this example, we set propertiesToUpdate to a dictionary containing one key, "done", with value NSNumber(bool: true). This means that every Item record will be set to done, which is what we're after.

Execute Batch Update Request

Even though batch updates bypass the managed object context, executing a batch update request is done by calling executeRequest(_:) on an NSManagedObjectContext instance. This method accepts one argument, an instance of the NSPersistentStoreRequest class. Because executeRequest(_:) is a throwing method, we execute the method in a do-catch statement.

Updating the Managed Object Context

Even though we hand the batch update request to a managed object context, the managed object context is not aware of the changes as a result of executing the batch update request. As I mentioned earlier, it bypasses the managed object context. This gives batch updates their power and speed. To remedy this issue, we need to do two things:

  • turn the managed objects that were updated by the batch update into faults
  • tell the fetched results controller to perform a fetch to update the user interface

This is what we do in the next few lines of the checkAll(_:) method in the do clause of the do-catch statement. If the batch update request is successful, we extract the array of NSManagedObjectID instances from the NSBatchUpdateResult object.

We then iterate over the objectIDs array, ask the managed object context for the corresponding NSManagedObject instance, and turn it into a fault by invoking refreshObject(_:mergeChanges:), passing in the managed object as the first argument. To force the managed object into a fault, we pass false as the second argument.

Fetching Updated Records

The last step is to tell the fetched results controller to perform a fetch to update the user interface. If this is unsuccessful, we catch the error in the catch clause of the do-catch statement.

While this may seem cumbersome and fairly complex for an easy operation, keep in mind that we bypass the Core Data stack. In other words, we need to take care of some housekeeping that's usually done for us by Core Data.

Step 4: Build & Run

Build the project and run the application in the simulator or on a physical device. Click or tap the bar button item on the right to check every to-do item in the list.

Checking All To-Do Items

Conclusion

Batch updates are a great addition to the Core Data framework. Not only does it answer a need developers have had for many years, it isn't difficult to implement as long as you keep a few basic rules in mind. In the next tutorial, we'll take a closer look at batch deletes, another feature of the Core Data framework that was added only recently.

Tags:

Comments

Related Articles