0

I am using RealmSwift for my new app. My Realm class has two primary keys. Just an example I have a Realm Model(Product) like this:-

class Product: Object, Mappable {

    dynamic var id: String? = nil
    dynamic var tempId: String? = nil
    dynamic var name: String? = nil
    dynamic var price: Float = 0.0
    dynamic var purchaseDate: Date? = nil

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

    //I want to do something like this
    override static func primaryKey() -> String? {
        return "id" or "tempId"
    }

    func mapping(map: Map) {
        id <- map["_id"]
        tempId <- map["tempId"]
        name <- map["name"]
        price <- map["price"]
        purchaseDate <- (map["purchaseDate"], DateFormatTransform())
    }

So I am creating an realm object in my device and storing into realm db with primary key tempId, as the actual primary key is the id, which is a server generated primary key is coming only after the report sync. So when I am sending multiple reports to the server with those tempId server response me back with actual id mapped with each tempId. As the report is not only created from my side so I can't keep tempId as the primary key. I thought of Compound primary key but it won't solve the problem.

So I want to create a primary key such as If id is there then that is the primary key else tempId is the primary key.

How to do this?

Kousik
  • 21,485
  • 7
  • 36
  • 59
  • 1
    I guess you need only one primary key ID. Use it instead of tempId, when server updates the primary key, replace your entire object with new object with new primary key i.e. ID. Delete the object with previous ID, add the object with new ID – Mohammad Sadiq Aug 11 '17 at 10:26
  • How do I figure out the primary key is actual key or not. In that case I have to maintain one extra param. – Kousik Aug 11 '17 at 10:39
  • You would be having some way to map the new key generated from server to the local primary key. It should be one time only. With your tempID example how were you trying to map? – Mohammad Sadiq Aug 11 '17 at 10:42
  • Map is taken care by the ObjectMapper – Kousik Aug 11 '17 at 10:44
  • No. By mapping I meant previously you were trying to use tempId, when the server gives you back the original ID how were you knowing that this is the key for this ID. Lets say tempID was '123', server generated Id for it was '765444', how would you know? Same way you can identify the ID generated with only one value. – Mohammad Sadiq Aug 11 '17 at 10:46
  • That was the problem. I was thinking how to automate it. Logic is as simple as just filter out the objects which match id/tempId and update, if no match just create. if there is only one primary key I don't have to write any code. realm.add(object: myObject, update: true) will do that. – Kousik Aug 11 '17 at 10:52

2 Answers2

2

What you need essentially is a computed property as a primary key. However, this isn't supported at the moment, only stored and managed realm properties can be used as primary keys. A workaround could be to define both id and tempId to have explicit setter functions and inside the setter function you also need to set another stored property, which will be your primary key.

If you want to change id or tempId don't do it in the usual way, but do it through their setter function.

Idea taken from this GitHub issue.

class Product: Object {
    dynamic var id:String? = nil
    dynamic var tempId: String? = nil

    func setId(id: String?) {
        self.id = id
        compoundKey = compoundKeyValue()
    }

    func setTempId(tempId: String?) {
        self.tempId = tempId
        compoundKey = compoundKeyValue() 
    }

    dynamic var compoundKey: String = ""
    override static func primaryKey() -> String? {
        return "compoundKey"
    }

    func compoundKeyValue() -> String {
        if let id = id {
            return id
        } else if let tempId = tempId {
            return tempId
        }
        return ""
    }
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Thanks. I thought of compound key but was not knowing that I can write if else inside that. This looks fine but I have one doubt. Suppose I have created a report with tempId before sync. After sync I will get the same report with both the keys `id` and `tempId`. As the sync report has id and same local report is not having the id . I feet this will not update the report, it will create a new one. – Kousik Aug 11 '17 at 11:18
  • 1
    If the two IDs are the same, the compound key will be the same as well, so this shouldn't happen, your object should be updated in this case and not created. You can run some tests, but this problem should not happen with this setup. – Dávid Pásztor Aug 11 '17 at 12:43
0
dynamic private var compoundKey: String = ""

required convenience init?(map: Map) {
  self.init()
  if let firstValue = map.JSON["firstValue"] as? String,
    let secondValue = map.JSON["secondValue"] as? Int {
    compoundKey = firstValue + "|someStringToDistinguish|" + "\(secondValue)"
  }
}
ZeroOne
  • 584
  • 4
  • 17