CoreData Batch Updates in Swift

Core Data, one of the most important Cocoa frameworks, received new interesting functionalities in iOS 8 and OS X 10.10.

Today, I am going to show you how to perform batch updates of the data contained in the Persistent Store. This will allow you to modify one or more properties of your entities with no need of loading the data into the Managed Object Context. Instead, batch updates are directly performed in the Persistent Store.

Until iOS 7, if you wanted to perform an update of a property of a set of managed objects, you had to execute three tasks: fetch, update and save. So, you have to perform a fetch request to bring the objects from the Persistent Store into the Managed Object Context. After that, you had to touch every managed object and update the properties with the new values. Finally, you had to save the results back into the Persistent Store.

This is quite straightforward to implement. However, in real applications the update of a large number of objects requires quite long processing time. Of course, you can use multiple contexts and thread confinement, but you really should know what to do, otherwise it is easy to mess with the main thread, blocking it and make the UI unresponsive.

The new batch updates approach

In iOS 8, Apple introduces a new API to perform batch updates directly on the Persistent Store. There is still some work you have to do to keep in sync the UI and your data, but the separation between the managed object context and the persistent store coordinator makes easier to keep the UI responsive. Additionally, the processing performance and memory usage of the batch updates are really impressive.

Let’s see how it works. First of all, Apple introduced a new method executeRequest:error: in the NSManagedObjectContext class. It is very similar to the executeFetchRequest:error: method that you can use to perform a fetch request to populate the Managed Object Context. Its first argument is a NSBatchUpdateRequest. This is a new class, subclass of the NSPersistentStoreRequest recently introduced in iOS 7, and provides very similar functionalities to its sibling NSFetchRequest. The batch request is composed of an entity (the entity containing the property or properties you want to update) and a predicate to define a subset of data you want to update. Eventually, you can also define a dictionary containing the properties you want to update and their new values.

Once created, the NSBatchUpdateRequest is passed to the executeRequest:error: method. After its execution, this method returns an NSBatchUpdateResult object (subclass of NSPersistentStoreResult), the result property of which contains the batch updates result value(s). You can define the type of results you want from the executeRequest:error:, when you define the NSBatchUpdateRequest. You choose among three types of results:

  • The success or failure of the batch update (NSStatusOnlyResultType)
  • The count of the updated objects (NSUpdatedObjectsCountResultType)
  • The Object ID of the updated objects (NSUpdatedObjectIDsResultType)

What you need to keep in mind is that the changes happen directly in the Persistent Store. They are not automatically reflected in the Managed Object Context. So, if you want to sync the UI, you have to collect the Object ID list returned by the executeRequest:error: method (using the NSUpdatedObjectIDsResultType) and pass it to the Managed Object Context using the refreshObject:mergeChanges: method (please, refer to the Apple documentation on how to use this method correctly).

Also, keep in mind that the validation rules are turned off while the batch update is running, so do not updates your data with wrong values.

Coding an example

Let’s build an example to understand better how batch updates work in Core Data for iOS 8. Start creating a new Xcode 6 project. Name it BatchUpdatesExample. Choose the Single View Application template. Then, select Swift as language and check “Use Core Data”.

First, let’s create the model of our data. Inside the Data Model Editor, add just a single entity Person and add an attribute name to it. The type of this attribute will be a string. Generate the class file for the entity. It should look like this:

Since we are using Swift, remember to specify the class name for the entity correctly. In this case, the class name in the Data Model Inspector should be BatchUpdatesExample.Person, including the name of the module.

Let’s generate some fake data. To make this example short and quick, open the AppDelegate.swift file and add the following chuck of code in the application:didFinishLaunchingWithOptions: method:

Run the application. Great, now you have 100 thousand records in the persistent store. Comment the above piece of source code, so that it won’t be executed again. In the same application:didFinishLaunchingWithOptions:, add the following two lines of code:

Finally, add these properties to the ViewController class:

The first property is a pointer to managed object context defined in the AppDelegate class. The second and the third properties are just two help variables we would need later for our experiments.

Let’s add first a method to the ViewController class to perform the classic fetch-update-save. First, we need to fetch the data and move them from the store to the context. Once in the context, we are going to loop on then and modify them one by one. Finally, we save the data back into the store. I am going to simplify the entire process to make this post short and I am going to remove some error handling in the following source code (i.e. you should not use this source code directly in your production apps):

In this piece of code, I create a fetch request (lines 1-3). Then, I execute the fetch request (line 4) to populate the Managed Object Context with objects coming from the Persistent Store. Then, I update the value of the property of each managed object (lines 5-7) and finally in line 8, I save the updated objects back to Persistent Store.

Let’s now overwrite the viewDidAppear method of the ViewController. Add the following method to the ViewController class:

Line 1 uses the batch flag to switch between the new batch update approach (lines 2-6) or the legacy one (line 7). In line 2 I create a batch update request. Then, I define the property I want to update (line 3). I create a dictionary with the keys representing the properties I want to updates and the values representing the new values of each property. In this particular case (line 3), I am just updating the attribute name of the Person entity. In line 4, I specify that the result type will be the number of the updated objects. You can additionally define which store this batch update is going to affect. In this example, I am using a single store, so I don’t need to specify one. In line 5, I finally execute the batch update request and log the result (line 6).

Performance

After implementing this small example, let’s give a look at the performance. First, let’s compare the processing time of the two approaches. I am going to run this code on an iPhone 5s and use the 100 thousand records previously created.

The legacy Fetch-Update-Save takes 7.2s to run on the iPhone 5s, while the new batch updates run in only 0.81 s.

Impressive is also the memory usage. As expected, since the batch updates run directly on the Persistent Store, the memory usage is incredibly lower than the old approach. The following image shows the memory usage of the two cases measured by the Memory Gauge in Xcode 6.

Conclusion

I showed you how to perform a batch updates in iOS 8 with the new executeRequest:error: method. Speed and memory performance have been also highlighted. I hope it will be useful.

Keep coding in swift or objective-c,

Geppy

iOS Consulting | INVASIVECODE

iOS Training | INVASIVECODE

(Visited 88 times, 1 visits today)