Core Data Features in iOS 8 (Part 1)
Last June, Apple introduced iOS 8. And every new version of iOS comes with a bunch of new APIs. Browsing the full list of API differences between iOS 7 and iOS 8, we came across Core Data new features: batch update requests and asynchronous fetch requests. In this article we will focus on the first type of requests.

Batch update requests
The first interesting feature in the new Core Data APIs is the introduction of batch update requests. Before iOS 8, there was no built-in way to update all your Core Data records to the same value. You had to do it manually and as we will see a little further, this could be time consuming. Now Core Data provides a generic and efficient way to perform such operation.
Apple introduced a new subclass of NSPersistentStoreRequest
called NSBatchUpdateRequest
. This class is used to perform a batch update in a persistent store, without loading any data into memory.
If we look in detail at the API, we notice that NSBatchUpdateRequest
instances have a property called propertiesToUpdate
. This property is used to specify the attributes of the entity to update, along with their final values.
There is also a resultType
property that is used to set the type of result that should be returned from the request. It can take three different values:
NSStatusOnlyResultType
(by default): the request does not return anythingNSUpdatedObjectIDsResultType
: the request returns the object IDs (NSArray
ofNSManagedObjectID
) of the rows that were updatedNSUpdatedObjectsCountResultType
: the request returns the number of rows that were updated
In practice: iOS 7 VS iOS 8
Let’s take an example to see the difference. Imagine we have a set of articles objects, and each article has a read
property that indicates if the current user has read the article.
Let’s assume we want to implement a feature that mark all articles as read.
In iOS 7, we would have written something like this:
- (void)markAllAsRead {
// Create fetch request for Article entity
NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Article"];
// Execute the fetch request and get the array of articles
NSArray * articles = [self.managedObjectContext executeFetchRequest:fetchRequest error:NULL];
// Update the managed objects one by one
for (Article * article in articles) {
article.read = @YES;
}
// Save the context to persist changes
NSError * error = NULL;
[self.managedObjectContext save:&error];
if (error) {
// Handle the error
}
}
Very straightforward. We fetch all the articles, iterate through all of them and set the read
property to @YES
. Finally we save the context to persist the data.
In iOS 8, though, we can now use an NSBatchUpdateRequest
to perform the same action.
- (void)markAllAsRead {
// Create the batch update request for Article entity
NSBatchUpdateRequest * batchUpdateRequest =
[NSBatchUpdateRequest batchUpdateRequestWithEntityName:@"Article"];
// Specify the properties to update and their final values
batchUpdateRequest.propertiesToUpdate = @{@"read": @YES};
// Set up the result of the operation
batchUpdateRequest.resultType = NSUpdatedObjectsCountResultType;
// Execute the request
NSError * error = NULL;
NSBatchUpdateResult * result =
(NSBatchUpdateResult *)[self.managedObjectContext executeRequest:batchUpdateRequest
error:&error];
if (!result) {
// Handle the error
} else {
// result is of type NSUpdatedObjectsCountResultType
NSAssert(result.resultType == NSUpdatedObjectsCountResultType, @"Wrong result type");
// Handle the result
NSLog(@"Updated %d objects.", [result.result intValue]);
}
}
Let’s look closer at what is happening here.
- First we create the
NSBatchUpdateRequest
with the entity we want to update. - We specify the properties to update via the
propertiesToUpdate
property. In our case we want to set theread
property to@YES
for all the articles. - We set the result type of the request to
NSUpdatedObjectsCountResultType
- Finally, we pass the request to the store with the method
[NSManagedObjectContext executeRequest:error:]
that returns aNSPersistentStoreResult
. This result is cast toNSBatchUpdateResult
to access the number of rows that were updated.
Performance
The two examples above perform the same action, but there is a big difference between them in term of performances. Let’s focus on what happened under the hood in both cases.
The iOS 7 example starts to load all the articles into memory with an NSFetchRequest
. In practice that means an SQL SELECT
request is sent to the store to retrieve all the articles. Then Core Data loads the articles into memory, creating N
NSManagedObject
where N
is the number of articles fetched. This step can be time consuming for large data sets.
Then all the managed objects are updated to set the read
property to YES
. Changes are persisted to the database when we save the context. Actually, N
SQL UPDATE
requests are generated, one request for each article.
In total, N + 1
SQL requests were sent to the persistent store.
However, the iOS 8 example do not use an NSFetchRequest
. There is no SELECT
request and no memory overhead. Instead, performing an NSBatchUpdateRequest
send only one UPDATE
SQL request to the store that takes care of updating all the records.
In total, only one request was sent to the persistent store.
Using batch update request avoids sending N
requests to the store and creating N
managed objects into memory. To get an idea of the overhead it represents, setting up a simple benchmark shows that updating 1000 articles is about 10 times faster with a batch request than with a classic fetch request.
Conclusion
Using NSBatchUpdateRequest
is a great improvement in terms of performance because it will not load any content in the current context and will only deliver the request to the store. That means your code relies directly on the database to update the records, and not on the NSManagedObject
objects anymore.
Note. As batch update requests do not create managed objects, there are no validations performed when you update your records. It’s up to you to verify in advance that the values are correct when passed to the store.
We're hiring!
We're looking for bright people. Want to be part of the mobile revolution?
Open positions