Search code examples
swiftswift4nsmanagedobjectcodablenscopying

Implementing Codable and NSManagedObject simultaneously in Swift


I have an order processing application I'm working on for my employers that was originally designed to get all data about orders, products, and clients dynamically from the API. So all of the objects and and all of the functions dealing with those objects are interacting in the app with a "pass by value" expectation, utilizing structs conforming to Codable.

I now have to cache pretty much all of these objects. Enter CoreData.

I have no desire to create two files for one object(one as a Codable struct and the other as an NSManagedObject class) and then trying to figure out how to convert one to another. So I want to implement both in the same file...while still somehow being able to use my "pass by value" code.

Perhaps this is impossible.

edit

I'm looking for something a bit simpler than rebuilding all my data structures from the ground up. I understand I'll have to do some alterations to make a Codable struct compatible with a NSManagedObject class. I'd like to avoid making a custom initializer that requires me to enter in every property by hand, because there's hundreds of them.


Solution

  • In the end, it sounds like there is no "good" solution when migrating from an API dynamic app without caching to a cached app.

    I decided to just bite the bullet and try the method in this Question: How to use swift 4 Codable in Core Data?

    EDIT:

    I couldn't figure out how to make that work so I used the following solution:

    import Foundation
    import CoreData
    
    /*
     SomeItemData vs SomeItem:
     The object with 'Data' appended to the name will always be the codable struct. The other will be the NSManagedObject class.
     */
    
    struct OrderData: Codable, CodingKeyed, PropertyLoopable
    {
        typealias CodingKeys = CodableKeys.OrderData
    
        let writer: String,
        userID: String,
        orderType: String,
        shipping: ShippingAddressData
        var items: [OrderedProductData]
        let totals: PaymentTotalData,
        discount: Float
    
        init(json:[String:Any])
        {
            writer = json[CodingKeys.writer.rawValue] as! String
            userID = json[CodingKeys.userID.rawValue] as! String
            orderType = json[CodingKeys.orderType.rawValue] as! String
            shipping = json[CodingKeys.shipping.rawValue] as! ShippingAddressData
            items = json[CodingKeys.items.rawValue] as! [OrderedProductData]
            totals = json[CodingKeys.totals.rawValue] as! PaymentTotalData
            discount = json[CodingKeys.discount.rawValue] as! Float
        }
    }
    
    extension Order: PropertyLoopable //this is the NSManagedObject. PropertyLoopable has a default implementation that uses Mirror to convert all the properties into a dictionary I can iterate through, which I can then pass directly to the JSON constructor above
    {
        convenience init(from codableObject: OrderData)
        {
            self.init(context: PersistenceManager.shared.context)
    
            writer = codableObject.writer
            userID = codableObject.userID
            orderType = codableObject.orderType
            shipping = ShippingAddress(from: codableObject.shipping)
            items = []
            for item in codableObject.items
            {
                self.addToItems(OrderedProduct(from: item))
            }
            totals = PaymentTotal(from: codableObject.totals)
            discount = codableObject.discount
        }
    }