Search code examples
swiftlistrealm

Realm Swift - Nested lists in JSON


I have this JSON response from my service, which I need to map in order to save it in my Realm database.

{
  "Customers": [
    {
      "ID": 1,
      "Name": "Customer 1",
      "Contacts": [
        {
          "ID": 1,
          "Name": "Contact A",
        },
        ...
      ]
    },
    {
      "ID": 2,
      "Name": "Customer 2",
      "Contacts": [
        {
          "ID": 1,
          "Name": "Contact B",
        },
        ...
      ]
    }
  ]
}

What important to note is that the Contacts array of each Customer can contain an element with ID 1. Not that surprising, since it is a different contact of another Customer. I know the ID of each Customer is unique.

My question is how I should define my Realm Model classes when I want to define a primary key for the Contacts. I think I need to use the ID of the Customer in some way (like a foreign key), but I cannot figure out how. Here are my models so far.

import RealmSwift
import ObjectMapper
import ObjectMapper_Realm

class Customers: Mappable {

    var customers = List<Customer>()

    required convenience init?(map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        customers <- (map["Customers"], ListTransform<Customer>())
    }
}

@objcMembers class Customer: Object, Mappable {

    dynamic var id: Int?
    dynamic var name: String?

    required convenience init?(map: Map){
        self.init()
    }

    override static func primaryKey() -> String? {
        return "id"
    }

    func mapping(map: Map) {
        id <- map["ID"]
        name <- map["Name"]
    }
}

@objcMembers class Contact: Object, Mappable {

    dynamic var id = 0
    dynamic var name: String?

    required convenience init?(map: Map){
        self.init()
    }

    /*override static func primaryKey() -> String? {
        return "id"
    }*/

    func mapping(map: Map) {
        id <- map["ID"]
        name <- map["Name"]
    }
}

When I use the code to override the primary key for the Contact model, I get an error saying that an object with ID 1 is already there. Any help is greatly appreciated.


Solution

  • The bottom line here is that primary keys must be unique.

    The Contact Realm object has a primary key but what you're trying to initialize the object with is not a unique key so you can't do that (you can't have multiple contacts with a key of 1).

    In other words, the Contact object is initialized with a primary key of 1 but then the next one is also being initialized with a key of 1 etc.

    I would suggest letting the Realm objects instantiate their own unique key with

    @objc dynamic var contact_id = UUID().uuidString 
    

    which is guaranteed to be unique and not use the ID being read in as the primary key.

     @objcMembers class Contact: Object, Mappable {
        dynamic var contact_id = UUID().uuidString
        dynamic var name: String?
    
        override static func primaryKey() -> String? {
            return "contact_id"
        }
    }
    

    Note the whole class is being managed with @objcMembers in the class def.