From REST API to CoreData in One Step

See how to convert fetched data from a REST API to entities and persist them to CoreData, the built-in local database of Apple.

Francesco Leoni

3 min read

So, your application retrieves data from a REST API and you need to decode it into custom objects and save them to CoreData, but you aren't sure how to do it.

I was recently in this position while building a bookmarks and rss feed manager.

While researching how to handle this, I came across multiple options, like creating a model that maps the JSON response and a separate one that is the actual CoreData entity to save and use throughout the app. But this seemed too much work to me, but it can suits other scenarios.

Then I saw this method that is exactly was I was looking for, quick and easy, with no model duplicates.

Steps

  1. Run a request to the endpoint you want to fetch the data from
  2. Decode the Data you received and save the context

    Done!

As you can see these are not a lot of steps. In this article I will explain only the second one.

So, with no further ado let's begin.

Setup

First, we are going to create a couple of helpers that will make our life easier.

The first one is a custom error that we will throw in case the context is not present.

enum DecoderConfigurationError: Error {

case missingManagedObjectContext

}

The second one is a CodingUserInfoKey that we will use to assign and retrieve the context.

extension CodingUserInfoKey {

static let managedObjectContext = CodingUserInfoKey(rawValue: "managedObjectContext")!

}

Decoding

Once we have those out of the way, we can start decoding our data.

We will use the default JSONDecoder and we will use the userInfo property to assign our NSManagedObjectContext to the CodingUserInfoKey.managedObjectContext key.

This way we are passing the context that we will use to create the NSManagedObject to the init(from:) .

do {

let decoder = JSONDecoder()

decoder.userInfo[.managedObjectContext] = PersistenceController.shared.context

let decodedObject = try decoder.decode(Object.self, from: data)

} catch {

// [Handle error...]

}

Note

The PersistenceController class is the implementation of the CoreData stack. You can find an example here.

Conforming to Decodable

Now we can make our NSManagedObject conform to decodable.

class Object: NSManagedObject, Decodable {

// [...]

}

Once we have done that we implement the init(from:) initialiser.

class Object: NSManagedObject, Decodable {

private enum CodingKeys: String, CodingKey {

case objectProperty

// [...]

}

required convenience init(from decoder: Decoder) throws {

// 1. Retrieve the context

guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext] as? NSManagedObjectContext else {

// 2. Throw an error in case the context is not present

throw DecoderConfigurationError.missingManagedObjectContext

}

// 3. Instantiate the CoreData entity

self.init(context: context)

// 4. Decode the object properties

let container = try decoder.container(keyedBy: CodingKeys.self)

self.objectProperty = try container.decode(String.self, forKey: . objectProperty)

// [Continue decoding properties...]

}

}

In the code above we set up the decoding method:

  1. We retrieve the NSManagedObjectContext that we passed earlier using the userInfo property.
  2. We throw an error in case the context is not present
  3. We instantiate the CoreData entity using the context we retrieved
  4. And we decode each property from the data we received from the API

Save the context

Finally we save the context to persist our decoded objects.

context.save()

Conclusion

In this way we can decode and persist our data in one single step. With no need to create a separate model that handles the decoding process.

I found this method really effective in my experience, I hope it will be useful to you as well.

If you have any doubt or question leave a comment and I'll be glad to reply.

If you have any question about this article, feel free to email me or tweet me @franceleonidev and share your opinion.

Thank you for reading and see you in the next article!

Share this article

Related articles


Combine CoreData and SwiftUI

See how to use CoreData database with SwiftUI. Syncing changes from CoreData to every View of your app.

5 min read

SwiftUICoreData

SwiftData the Successor of CoreData Explained with SwiftUI

Discover the SwiftData framework built on top of CoreData. Save and fetch data locally. Available for Xcode 15, Swift and SwiftUI.

5 min read

SwiftUICoreData

Fix “Some files could not be transferred (code 23)“ Error in Xcode

Discover how to fix the 'Some files could not be transferred (code 23)' error connected to Cocoapods.

1 min read

Q&AXcodeErrorsCocoapods