0

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.

A. L. Strine
  • 611
  • 1
  • 7
  • 23
  • Related: [How to use swift 4 Codable in Core Data?](https://stackoverflow.com/questions/44450114/how-to-use-swift-4-codable-in-core-data). – Martin R Feb 28 '19 at 03:51
  • I've looked into that and it would require a lot of refactoring of my code that I would like to avoid in my specific situation, if possible. – A. L. Strine Feb 28 '19 at 03:55
  • Can you post a sample code? NSManagedObject can not be a struct, but you already figured that put yourself. I would separate it, create a struct that handles the incoming data (decoding, setting default values if the data is not valid etc). And then a class that encapsulates the saving process. After all they are two different concerns. – PaFi Feb 28 '19 at 03:57
  • @PaFi That's a great solution when building from the ground up, haha. I suppose I could wrap every struct object in memory in a NSManagedObject class just before the application closes, but that is a less than optimal solution for this stage of development. – A. L. Strine Feb 28 '19 at 04:13
  • 1
    From my experience the most efficient solution is the solution in the linked question. Adopt `Codable` in each `NSManagedObject` subclass and implement the `init` and `encode` methods. Even relationships can be handled quite comfortable. – vadian Feb 28 '19 at 08:25
  • Take a look at this new issue I ran into: https://stackoverflow.com/questions/54935422/coredata-and-codable-class-compiler-error-self-init-isnt-called-on-all-paths – A. L. Strine Feb 28 '19 at 23:19

1 Answers1

1

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
    }
}
A. L. Strine
  • 611
  • 1
  • 7
  • 23