Modern, Efficient Core Data

In this tutorial, you’ll learn how to improve your iOS app thanks to efficient Core Data usage with batch insert, persistent history and derived properties. By Andrew Tetlaw.

4.5 (13) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Step 6: Merging Important Changes

OK, you've added all of the optimizations you need to make sure that your view context processes changes from only the most relevant transactions. All that's left to do is merge those changes into the view context to update the UI. And that is relatively simple.

Add the following method to your PersistenceController:

private func mergeChanges(from transactions: [NSPersistentHistoryTransaction]) {
  let context = viewContext
  // 1
  context.perform {
    // 2
    transactions.forEach { transaction in
      // 3
      guard let userInfo = transaction.objectIDNotification().userInfo else {
        return
      }

      // 4
      NSManagedObjectContext
        .mergeChanges(fromRemoteContextSave: userInfo, into: [context])
    }
  }
}

Here what's going on in the code above:

  1. You make sure to do the work on the view context's queue, using perform(_:).
  2. You loop over each transaction passed to this method.
  3. Each transaction contains all the details of each change, but you need it in a form you can pass to mergeChanges(fromRemoteContextSave:into:): a userInfo dictionary. objectIDNotification().userInfo has just the dictionary you need.
  4. Passing that to mergeChanges(fromRemoteContextSave:into:) will bring the view context up to date with the transaction changes.

Remember the query generation you set previously? One of the effects of the mergeChanges(fromRemoteContextSave:into:) method is to update the context's query generation. Handy!

All that remains is to call your new method. Add the following line to processRemoteStoreChange(_:) just before the call to print(_:) (you can also remove that call to print(_:) if you'd like!):

self.mergeChanges(from: transactions)

The process changes method now filters the transactions and passes only the most relevant ones to the mergeChanges(from:) method.

Build and run!

Forget the console, check out your app. Refresh twice and you should see nothing happen the second time because no work is required. Then, delete a fireball and then tap the refresh button. You'll see it appear again!

Fireball list is up to date

Adding Derived Attributes

You're able to add fireballs to groups, so it'd be nice to show the fireball count in the group list as well.

Derived attributes are a recent addition to Core Data that allow you to create an entity attribute that's computed from child entity data each time the context is saved and stored in the persistent store. This makes it efficient, because you don't need to recompute it each time it's read.

You create a derived attribute in your managed object model. Open FireballWatch.xcdatamodeld and select the FireballGroup entity. Find the Attributes section and click the plus button to add a new attribute. Call it fireballCount and set the type to Integer 64.

In the Data Model inspector on the right, check the Derived checkbox, which reveals the Derivation field. In this field, type the following:

fireballs.@count

This uses the predicate aggregate function @count and acts on the existing fireballs relationship to return the count of how many fireballs are child entities of this group.

Derived attributes in Xcode

Remember to save your managed object model.

Note: Derived attributes are, as of Xcode 12, limited to a few specific use cases. You can find out what's possible in the Apple documentation.

All that's left to do is display the count.

Open FireballGroupList.swift, located in the View group, and find the following line:

Text("\(group.name ?? "Untitled")")

Replace it with the following:

HStack {
  Text("\(group.name ?? "Untitled")")
  Spacer()
  Image(systemName: "sun.max.fill")
  Text("\(group.fireballCount)")
}

This simply adds an icon and the fireball count to each row. Build and run to see how it displays:

Groups list with fireball count

Perfect!

Where to Go From Here?

Well done! Take a step back and admire your work. You should feel proud knowing the world is a little safer from alien invasion because of your app.

You did it. You saved Earth!

You can download the completed project using the Download Materials button at the top or bottom of this tutorial.

If you're looking for a challenge, try adding code to delete the unnecessary transaction history after it's already been processed, to save the history from growing indefinitely. There's a handy method for the job: NSPersistentHistoryChangeRequest.deleteHistoryBefore(_:).

If you're looking to learn even more about Core Data, I recommend:

I hope you enjoyed this Core Data tutorial. If you have any questions or comments, please join the forum discussion below.

Stay vigilant!

I will return, pathetic human!