0

I'm trying to save JSON to CoreData but the JSON is a little bit complex with many array objects. I could only save and display simple attributes like String, Int... but if I add attributes with Transformable type like [Currency], [Double], the app will crash.

Could you please show me the right way to implement this?

My Data Model

struct Currency: Decodable {
    let code, name, symbol: String?
}

struct Language : Decodable {
    let name : String?
}

struct WorldData: Identifiable, Decodable {
    var id : Int {
        return population
    }
    var name : String?
    ...
    var latlng : [Double]?
    var currencies : [Currency]?
    var languages : [Language]
    ...
}

My Manual NSManagedObject subclass

extension CDCountry {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<CDCountry> {
        return NSFetchRequest<CDCountry>(entityName: "CDCountry")
    }

    @NSManaged public var name: String?
    @NSManaged public var latlng: NSObject?
    @NSManaged public var currencies: NSObject?
    @NSManaged public var languages: NSObject?
    ...

    public var wrapperLatlng : NSObject {
        latlng ?? ["Unknown error"] as NSObject
    }
    
    public var wrapperCurrencies : NSObject {
        currencies ?? ["Unknown error"] as NSObject
    }
...

and how I save loaded JSOn to CoreData

static func loadDataToCD(moc: NSManagedObjectContext) {
        loadDataFromJSON { (countries) in
            DispatchQueue.main.async {
                var tempCountries = [CDCountry]()
                for country in countries {
                    let newCountry = CDCountry(context: moc)
                    
                    ...
                    newCountry.currencies  = country.currencies as NSObject?
                    newCountry.languages = country.languages as NSObject
                    newCountry.latlng = country.latlng as NSObject?
                    newCountry.name = country.name
                    ...
                    tempCountries.append(newCountry)
                }
                do {
                    try moc.save()
                } catch let error {
                    print("Could not save to CD: ", error.localizedDescription)
                }
            }
Duc Dang
  • 186
  • 1
  • 14
  • 1
    You're using `Codable`, but transformable attributes require `NSCoding`. Those protocols do similar things but are not compatible. `Codable` types can't be used with transformable attributes. – Tom Harrington Sep 08 '20 at 20:41
  • do you have any suggestion on how to make it compatible? Thank you @Tom Harrington – Duc Dang Sep 08 '20 at 21:04

1 Answers1

1

There is no easy way to answer your question but I can point you to some resources.

There is a good example on decoding JSON within the Landmark.swift file included in the "Handing User Input" Tutorial that Apple provides for SwiftUI. That might help with the basics. https://developer.apple.com/tutorials/swiftui/handling-user-input

The tutorial uses custom Coordinates and Category classes that can likely mimic the process for your Currency and Language objects. But of course that would be for a Non-Managed Object.

To do it Managed it would be best to create CoreData objects for Currency and Language and join them as a relationship in CoreData vs making them a Transformable.

Then you can follow the answer for this older question that was answered very throughly Save complex JSON to Core Data in Swift

You can also look at this question Swift And CoreData With Custom Class As Transformable Object

lorem ipsum
  • 21,175
  • 5
  • 24
  • 48