How to use swift 4 Codable in Core Data?

You can use the Codable interface with CoreData objects to encode and decode data, however it’s not as automatic as when used with plain old swift objects. Here’s how you can implement JSON Decoding directly with Core Data objects:

First, you make your object implement Codable. This interface must be defined on the object, and not in an extension. You can also define your Coding Keys in this class.

class MyManagedObject: NSManagedObject, Codable {
    @NSManaged var property: String?

    enum CodingKeys: String, CodingKey {
       case property = "json_key"
    }
}

Next, you can define the init method. This must also be defined in the class method because the init method is required by the Decodable protocol.

required convenience init(from decoder: Decoder) throws {
}

However, the proper initializer for use with managed objects is:

NSManagedObject.init(entity: NSEntityDescription, into context: NSManagedObjectContext)

So, the secret here is to use the userInfo dictionary to pass in the proper context object into the initializer. To do this, you’ll need to extend the CodingUserInfoKey struct with a new key:

extension CodingUserInfoKey {
   static let context = CodingUserInfoKey(rawValue: "context")
}

Now, you can just as the decoder for the context:

required convenience init(from decoder: Decoder) throws {

    guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }
    guard let entity = NSEntityDescription.entity(forEntityName: "MyManagedObject", in: context) else { fatalError() }

    self.init(entity: entity, in: context)

    let container = decoder.container(keyedBy: CodingKeys.self)
    self.property = container.decodeIfPresent(String.self, forKey: .property)
}

Now, when you set up the decoding for Managed Objects, you’ll need to pass along the proper context object:

let data = //raw json data in Data object
let context = persistentContainer.newBackgroundContext()
let decoder = JSONDecoder()
decoder.userInfo[.context] = context

_ = try decoder.decode(MyManagedObject.self, from: data) //we'll get the value from another context using a fetch request later...

try context.save() //make sure to save your data once decoding is complete

To encode data, you’ll need to do something similar using the encode protocol function.

Leave a Comment