4

I want to save the JSON Result from web service to Core data, following is the code for JSON parsing.

if let jsonResult = try JSONSerialization.jsonObject(with: JSONData!, options: [.mutableContainers]) as? [String: AnyObject]

If i prints jsonResult, following is the output

["Code": 00, "Desc": success, "iinData": <__NSArrayM 0x1c02531a0>
{
  name = “AAA”;
  iin = 123456;
  isOn = 0;
},
{
  name = "Allahabad Bank";
  iin = 608112;
  isOn = 0;
},
)

I can able to insert Code, Desc into Entity1, but how to insert innData into Entity2 .

Entity1 Structure Entity2 Structure

Following is the code for inserting JSON result into core data

func createEntity1From(dictionary: [String: AnyObject]) -> NSManagedObject? {
    let context = CoreDataStack.sharedInstance.persistentContainer.viewContext
    if let Entity1 = NSEntityDescription.insertNewObject(forEntityName: “ParamDownload”, into: context) as? ParamDownload {
        Entity1.Code= dictionary[“name”] as? String
        Entity1.desc = dictionary[“desc”] as? String
        return Entity1
    }
}
user7413163
  • 205
  • 1
  • 4
  • 15
  • Use convert to data? Nskeyedarchieve? – E.Coms Sep 28 '18 at 13:07
  • What did you do to save the parsed JSON to CoreData? It seems you have only used `JSONSerialization` to decode, but did nothing to convert the data to the CoreData-format you created. – Fabian Sep 28 '18 at 13:28
  • 1
    Thanks or your reply. I updated the code for inserting the data into entity1. Can you please explain me how to insert innData into Entity2. As this is one to many relationship datas. – user7413163 Oct 08 '18 at 08:15

3 Answers3

9

It's quite easy to decode the JSON directly into Core Data with Decodable

  • First of all create extensions of CodingUserInfoKey and JSONDecoder to be able to pass the managed object context

    extension CodingUserInfoKey {
        static let context = CodingUserInfoKey(rawValue: "context")!
    }
    
    extension JSONDecoder {
        convenience init(context: NSManagedObjectContext) {
            self.init()
            self.userInfo[.context] = context
        }
    }
    
  • Add conformance to Decodable in both classes

    class Name: NSManagedObject, Decodable {
    
    class ParamDownload: NSManagedObject, Decodable {
    
  • In the class Name (not the extension) add

    private enum CodingKeys: String, CodingKey { case name, iin, isOn }
    
    required convenience init(from decoder: Decoder) throws {
        guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError("NSManagedObjectContext is missing") }
        let entity = NSEntityDescription.entity(forEntityName: "Name", in: context)!
        self.init(entity: entity, insertInto: context)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        iin = try values.decode(String.self.self, forKey: .iin)
        isOn = try values.decode(Bool.self.self, forKey: .isOn)
    }
    
  • In the class ParamDownload (not the extension) add

    private enum CodingKeys: String, CodingKey { case code = "Code", desc = "Desc", names = "iinData" }
    
    required convenience init(from decoder: Decoder) throws {
        guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError("NSManagedObjectContext is missing") }
        let entity = NSEntityDescription.entity(forEntityName: "ParamDownload", in: context)!
        self.init(entity: entity, insertInto: context)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        code = try values.decode(String.self, forKey: .code)
        desc = try values.decode(String.self, forKey: .desc)
        names = try values.decode(Set<Name>.self, forKey: .names)
        names.forEach { $0.pd = self }
    }
    

To decode the JSON create the Decoder with the convenience initializer

let decoder = JSONDecoder(context: CoreDataStack.sharedInstance.persistentContainer.viewContext)

the init methods handle the relationships.

I recommend to declare the Core Data attributes as much non-optional as possible.
If an attribute must be remain optional replace decode with decodeIfPresent.

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thanks for your reply. Its working fine with some changes . – user7413163 Oct 10 '18 at 11:35
  • Thanks, great solution – Adelmaer Sep 14 '21 at 18:26
  • @vadian The mechanics makes sense - the inits create an entity, initialise the properties, and set the relationship where appropriate. But how are you actually initialising the decoding? i.e. `#what?# = decoder.deocde(#what?#.self, from: data)`. Is the returned data structure even needed, or just a `.save()` as the data is already committed to context? – flanker Aug 25 '23 at 19:19
  • @flanker The record is created in the `self.init(entity: entity, insertInto: context)` line and the context (passed by the JSONDecoder) needs to be saved. – vadian Aug 26 '23 at 15:27
  • @vadian Thanks. Get that bit. It was more how to do you get the json into the decoder in the first place. Just had a quick play and `let _ = decoder.decode[MyType].self, from: jsonAsData); try? moc.save()` did the job, but it seems inelegant and I was wondering if there was a smarter way of initialising he decoding? – flanker Aug 26 '23 at 17:21
  • Sorry I don’t get it. The first snippet is the extension to put the context in the `userInfo` dictionary. The JSON is decoded directly into the `NSManagedObject` instance in the convenience initializer. Yes, just decoding `[MyType].self` does the job and it’s rather elegant. – vadian Aug 26 '23 at 23:01
0

I save JSON by converting it in into Data and save that Data in CoreData

Following Code works for me......

func saveSpotsLocation(model: SpotsModel, packageId: String, regionName: String) {
  let newUser = NSEntityDescription.insertNewObject(forEntityName: "SpotsDetail", into: context)
  do {
    let data = NSKeyedArchiver.archivedData(withRootObject: model.dictionaryRepresentation())
    newUser.setValue(data, forKey: "data")
    newUser.setValue(packageId, forKey: "packageId")
    newUser.setValue(regionName, forKey: "regionName")
    try context.save()
  } catch {
    print("failure")
  }
}
adiga
  • 34,372
  • 9
  • 61
  • 83
Rahul Gusain
  • 269
  • 3
  • 6
-1

This also worked fine for me,

private func createParamDownloadEntityFrom(dictionary: [String: AnyObject]) -> NSManagedObject? {
    let context = CoreDataStack.sharedInstance.persistentContainer.viewContext

    if let paramEntity = NSEntityDescription.insertNewObject(forEntityName: “ParamDwonload”, into: context) as? ParamDownload {
        paramEntity.code = dictionary[“code”] as? String
        paramEntity.desc = dictionary[“desc”] as? String
        let innData = dictionary["iinData"] as! NSArray


        for i in 0..<(innData.count-1) {

            if let nameEntity = NSEntityDescription.insertNewObject(forEntityName: Name
                , into: context) as? Name {

                if let val = innData[i] as? [String: Any] {
                    nameEntity.bankName = val[“name"] as? String
                    nameEntity.iin = val[“iin"] as? String
                    if let isOn = Int16(val[“isOn"] as! String) {
                        nameEntity.isOnus = isOn
                    }
                    paramEntity.addToNames(nameEntity)
                }

            }
        }
        return paramEntity
    }
user7413163
  • 205
  • 1
  • 4
  • 15