Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate ids for objects in RealmSwift?

Tags:

swift

realm

I am using RealmSwift. What is the best / canonical way to generate ids for objects?

Here is what I came up with:

class MyObject: Object {
    dynamic var id = ""
    dynamic var createdAt = NSDate()

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

    func save() {
        let realm = try! Realm()

        if(self.id == "") {
            while(true) {
                let newId = NSUUID().UUIDString
                let saying = realm.objectForPrimaryKey(MyObject.self, key: newId)
                if(saying == nil) {
                    self.id = newId
                    break
                }
            }
        }

        try! realm.write {
            realm.add(self)
        }
    }
}

I wanted a function that persists the object to Realm and either overwrites or creates a new one based on the id. This seems to work ok, but I wasn't sure if there was something built into realm to do this. Or is there a better way?

Thanks.

like image 346
Eric Conner Avatar asked May 14 '16 14:05

Eric Conner


2 Answers

I know this is a few months old, but this is how I implement auto incrementing Primary Keys.

This code is untested, but you'll get the general idea

class MyObject: Object {
    /**
    Primary Key

    Since you can't change the primary key after the object is saved,
    we'll use 0 to signify an unsaved object. When we set the primary key,
    we'll never use 0.
    */
    dynamic var id: Int = 0

    /**
    Some persisted value
    */
    dynamic var someString: String?

    var nextID: Int {
        do {
            let realm = try Realm()

            /// I would use .max() but Realm only supports String and signed Int for primary keys
            /// so using overflow protection, the `next` primary key would be Int.min if the
            /// current value is Int.max
            var id = realm.objects(MyObject.self).sorted("id", ascending: true).last?.id ?? 0

            /// add 1 to id until we find one that's not in use... skip 0
            repeat {
                id = Int.addWithOverflow(id, 1).0 /// Add with overflow in case we hit Int.max
            } while id == 0 || realm.objectForPrimaryKey(MyObject.self, key: id) != nil

            return id
        } catch let error as NSError {
            /// Oops
            fatal(error: error.localizedDescription)
        }
    }

    convenience init(someString: String?) {
        self.init()

        id = nextID
        self.someString = someString
        save()
    }

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

    func save() {
        /// Gotta check this in case the object was created without using the convenience init
        if id == 0 { id = nextID }

        do {
            let realm = try Realm()
            try realm.write {
                /// Add... or update if already exists
                realm.add(self, update: true)
            }
        } catch let error as NSError {
            fatalError(error.localizedDescription)
        }
    }
}

The process for creating a unique string ID (IE: a UUID) is very similar:

class MyObject: Object {
    /**
    Primary Key
    */
    dynamic var id: String = ""

    /**
    Some persisted value
    */
    dynamic var someString: String?

    var nextID: String {
        do {
            let realm = try Realm()

            var id: String = NSUUID().UUIDString

            /// NSUUID().UUIDString almost always makes a unique ID on the first try
            /// but we'll check after we generate the first one just to be sure
            while realm.objectForPrimaryKey(MyObject.self, key: id) != nil {
                id = NSUUID().UUIDString
            }

            return id
        } catch let error as NSError {
            /// Oops
            fatal(error: error.localizedDescription)
        }
    }

    convenience init(someString: String?) {
        self.init()

        id = nextID
        self.someString = someString
        save()
    }

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

    func save() {
        /// Gotta check this in case the object was created without using the convenience init
        if id == "" { id = nextID }

        do {
            let realm = try Realm()
            try realm.write {
                /// Add... or update if already exists
                realm.add(self, update: true)
            }
        } catch let error as NSError {
            fatalError(error.localizedDescription)
        }
    }
}
like image 53
Keith Avatar answered Oct 20 '22 01:10

Keith


Realm(Swift) does not currently support auto-incrementing primary keys. You can set a primary like you are above, but for auto-incrementing and unique keys, there are a couple routes that you can go:

  1. UUID (like you have above)
  2. Query the max "id" (primary key) of your object and set the object to be inserted as id + 1. Something like...

    let id = realm.objects(MyObject).max("id") + 1

  3. Create your own hash signature (one potential example: SHA256(epoch timestamp + SHA256(object.values))

like image 32
Christopher Harris Avatar answered Oct 20 '22 01:10

Christopher Harris