fabernovel loader

Oct 6, 2014 | 4 min read

Tech

Core Data Features in iOS 8 (Part 1)

Pierre Felgines

Developer Senior


FABERNOVEL TECHNOLOGIES
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 anything
  • NSUpdatedObjectIDsResultType: the request returns the object IDs (NSArray of NSManagedObjectID) of the rows that were updated
  • NSUpdatedObjectsCountResultType: 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.

  1. First we create the NSBatchUpdateRequest with the entity we want to update.
  2. We specify the properties to update via the propertiesToUpdate property. In our case we want to set the read property to @YES for all the articles.
  3. We set the result type of the request to NSUpdatedObjectsCountResultType
  4. Finally, we pass the request to the store with the method [NSManagedObjectContext executeRequest:error:] that returns a NSPersistentStoreResult. This result is cast to NSBatchUpdateResult 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.

logo business unit

FABERNOVEL TECHNOLOGIES

150 talents to face technological challenges of digital transformation

next read