jsonswiftrealmswifty-jsonrealm-database

Successfully write JSON to Realm DB using swiftyJSON in Swift


I am trying to write to a Realm DB from JSON file (using swiftyJSON) get the following error:

libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value '{ "vehicle" : true, "viewable" : true, "id" : 0, "weapon" : false, "genres" : "[Fantasy, General]", "name" : "Car" }' to initialize object of type 'Item': missing key 'id''

My JSON file is structured as follows:

  [
  {
    "id": 0,
    "name": "Car",
    "genres": "[Fantasy, General]",
    "viewable": true,
    "weapon": false,
    "vehicle": true
  },
  {
    "id": 1,
    "name": "Truck",
    "genres": "[General]",
    "viewable": true,
    "weapon": false,
    "vehicle": true
  },
]

My Realm DB Class is:

class Item: Object {
    @objc dynamic var id: Int = 0
    @objc dynamic var name = ""
    let genres = List<String>()
    @objc dynamic var visable: Bool = false
    @objc dynamic var weapon: Bool = false
    @objc dynamic var vehicle: Bool = false
    
    override static func primaryKey() -> String? {
        return "id"
    }
    
    override static func indexedProperties() -> [String] {
        return ["genre", "visable"]
    }
    
}

and the code to write the JSON to the RealmDB is as follows:

func jsonAdd() {

    if let path = Bundle.main.path(forResource: "Data", ofType: "json") {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
            let json = JSON(data)
            
            for (index,subJson):(String, JSON) in json {
                
                do {
                    try realm.write {
                        
                        realm.create(Item.self, value: subJson, update: .modified)
                        
                    }
                    
                } catch let error as NSError {
                    print("Unable to write")
                    print(error)
                }
                
            }
            
        } catch {
            print("Error")
        }
        
    }
}

I think the issue is a JSON mapping one but any help will be greatly appreciated to write the JSON to the Realm DB including the genres as a List.

NB: I've managed to write to the Realm DB using the following code (based off the Realm documentation) before the 'Write' function but cannot seem to get it to work for my JSON structure.

 let newdata = "{\"id\": 0, \"name\": \"Car\", \"genres\": [\"Fantasy\",\"General\"], \"viewable\": true, \"weapon\": false, \"vehicle\": true}".data(using: .utf8)!
    
 let json = try! JSONSerialization.jsonObject(with: newdata, options: [])

Thank you!


Solution

  • Solved through the following approach:

    1. Slight change to my JSON structure as follows:

       [
         {
           "id": 0,
           "name": "Car",
           "genres": ["Fantasy", "General"],
           "viewable": true,
           "weapon": false,
           "vehicle": true
         },
         {
           "id": 1,
           "name": "Truck",
           "genres": ["Fantasy", "General"],
           "viewable": true,
           "weapon": false,
           "vehicle": true
         }
       ]
      
    2. Created a new Genres Object to better handle the list

       class Genres: Object {
           @objc dynamic var id = 0
           @objc dynamic var name = ""
      
           override static func primaryKey() -> String? {
               return "id"
           }
       }
      
       class Item: Object {
           @objc dynamic var id = 0
           @objc dynamic var name = ""
           var genres = List<Genres>()
           @objc dynamic var viewable: Bool = false
           @objc dynamic var weapon: Bool = false
           @objc dynamic var vehicle: Bool = false
      
           override static func primaryKey() -> String? {
               return "id"
           }
      
           override static func indexedProperties() -> [String] {
               return ["genres", "viewable"]
           }
      
       }
      

    3)Crucially in the jsonADD function updated the code to create a new Item Object then mapped the JSON values to that object before attempting to write to the Realm DB:

    for (key,subJson):(String, JSON) in jsonObjects {
            
            let thisItem = Item()
            thisItem.id = subJson["id"].intValue
            thisItem.name = subJson["name"].stringValue
            thisItem.visable = subJson["viewable"].boolValue
            thisItem.weapon = subJson["weapon"].boolValue
            thisItem.vehicle = subJson["vehicle"].boolValue
            
            let genreArray = subJson["genres"].arrayValue
            
            for genre in genreArray {
                let string = genre.stringValue
                let predicate = NSPredicate(format: "name = %@", string)
                
                if let foundGenre = realm.objects(Genres.self).filter(predicate).first {
                    thisItem.genres.append(foundGenre)
                }
            }
            
            do {
                try realm.write {
                    realm.create(Item.self, value: thisItem, update: .modified)
                }
                
            } catch let error as NSError {
                print("Unable to write")
                print(error)
            }
        }