Paving A Road Between JSON And CoreData

A short guide to ingesting JSON responses in CoreData

Jonathancbadger
Geek Culture

--

Photo by Joshua Sortino on Unsplash

CoreData, Apple’s framework for data persistence on iOS, is battle-tested, fully-featured and integrated with other frameworks such as CloudKit and SwiftUI, but has a steep learning curve. In this post I will go over the steps needed to move JSON data from a web request to a persistence store in CoreData.

Introduction

CoreData is a large and complex framework with lots of moving parts. For this article I am going to assume you have at least a bit of experience with CoreData. A few intro articles and/or a starter project or two should be sufficient. For brevity (and my own sanity), I am going to forgo a step-by-step project tutorial and just hit the highlights.

There is a sample project called JSONToCoreData that I wrote for this article. Feel free to download it from GitHub, fire up XCode, and follow along.

Outline

  • Sample JSON And CoreData Model
  • Decoding JSON
  • Updating the persistence store
  • Conclusion

Sample JSON And CoreData Model

Let’s start by describing some toy data we can work with and an associated model in CoreData. The data will come from {JSON} Placeholder, which provides a free fake API and data for testing. One of endpoints they offer, /posts, supplies JSON that looks like this:

As you can see the JSON contains a list of posts, each of which has four attributes. The corresponding CoreData model looks like this:

The Posts data model contains one entity, Post, that has four attributes (body: String, id: Int64, title: String, userId: Int64) just like the JSON. A few quick notes: 1. In the inspector (far right) the Codegen parameter is automatically building a subclass of NSManagedObject called Post that we can call from anywhere in our code, 2. I have added a constraint on the Post entity (for the id attribute) that will ensure no duplicate posts are added to the data store on subsequent updates.

Decoding JSON

Sow how do get from JSON to CoreData? Let’s start by creating a struct called PostData that conforms to the Decodable protocol.

We can decode the Data we receive from a URLSession.dataTask using JSONDecoder().decode([PostData].self, from: data). A complete function for our REST API call might look something like:

Note for the Decodable protocol basic datatypes such as Int and String are handled for you automatically, but with poorly formed JSON or more complex data types you may need to directly implementinit(from decoder: Decoder). For more about the decoding you can check out:

Before we move on, we are also going to add an extension to PostData and add a function called propertyDictionary:

This function uses introspection (Mirror) to loop through the properties of PostData returning a dictionary of [String: Any]. Keep this in the back of your mind as we will use it for batch insertion in a moment.

Updating the persistence store

Remember how I said that our CoreData Model automatically created a subclass of NSManagedObject called Post for us? You may be tempted then to loop through [PostData] and create NSManagedObjects using Post(context: viewContext) saving the context when you were finished. This would probably work fine for small updates, but for large data dumps it creates a ton of overhead that can cause some significant performance issues. A better technique is to interact directly with the persistence store (sqlite database) using NSBatchInsertRequest. Have a look at this sample code:

Lets take a careful walk through this section. First we create a new NSManagedObjectContext that operates on a background thread (see repo for details). We set the .mergePolicy to overwrite any duplicate posts already in the datastore using our most recent data and set the .undoManager to nil. Next we break down the data into batches to further reduce the overhead. (Not necessary in this example, but handy for large data dumps). We then use NSBatchInsertRequest to insert our data. Remember that little extension .propertyDictionary we wrote on PostData a minute ago? We are using it to create dictionaryObjects (the type is [[String: Array]]), a variable we are passing into the objects parameter of NSBatchInsertRequest. In the last step we submit the batch usingprivateContext.execute(batchInsertRequest), checking for errors before moving on. And that’s it! No need to call privateContext.save()as the changes have been directly applied to the backing store.

Conclusion

I hope you found this short tutorial on moving JSON data into a CoreData model useful. If you want to dive a little deeper into how changes on the persistence store are updated in SwiftUI you can take a look at Loading and Displaying A Large Data Feed from Apple. It’s a bit more complicated than what I have shown here, but provided the basis for the sample app. Lastly, if you enjoyed this article and want more, hit that little green follow button:)

Resources

--

--

Jonathancbadger
Geek Culture

Pharmacist, data scientist, Apple developer, and maker.