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.
• 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
- Run a
request
to the endpoint you want to fetch the data from - Decode the
Data
you received and save thecontext
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:
- We retrieve the
NSManagedObjectContext
that we passed earlier using theuserInfo
property. - We throw an error in case the context is not present
- We instantiate the CoreData entity using the
context
we retrieved - 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