RealmのIDを自動採番(オートインクリメント)する方法

  • 0
    いいね
  • 0
    コメント

    はじめに

    Realmにはauto incrementの機構がありません。
    なので、僕なりに工夫したことを残しておきます。

    なお、本コードはスレッドセーフで使うことを前提としています。
    もし、非同期で同時のタイミングで保存する可能性がある場合はsynchronizedなどの処理を施してください。

    コード

    何はともあれコードは以下になります。

    class RObject: Object {
        // ID
        dynamic var id = 0
    
        // データを保存。
        func save() {
            let realm = try! Realm()
            if realm.isInWriteTransaction {
                if self.id == 0 { self.id = self.createNewId() }
                realm.add(self, update: true)
            } else {
                try! realm.write {
                    if self.id == 0 { self.id = self.createNewId() }
                    realm.add(self, update: true)
                }
            }
        }
    
        // 新しいIDを採番します。
        private func createNewId() -> Int {
            let realm = try! Realm()
            return (realm.objects(type(of: self).self).sorted(byKeyPath: "id", ascending: false).first?.id ?? 0) + 1
        }
    
        // プライマリーキーの設定
        override static func primaryKey() -> String? {
            return "id"
        }
    }
    

    使い方

    Modelオブジェクトを以下のように定義します。

    class User: RObject {
        // 名前
        dynamic var name = ""
    }
    

    上記のクラスを保存する場合

    let user = User()
    user.name = "hoge"
    user.save()
    

    更新もしくは新規登録する場合

    let realm = try! Realm()
    let user = realm.objects(User.self).first ?? User()
    try! realm.write {
        user.name = "hoge"
        user.save()
    }
    

    最後に

    この方法を使えば全てのオブジェクトにidが自動的に付与されるのでRailsのように使えます。(言い過ぎ
    注意点として、現在のバージョン(2.5.0)の時点では親子関係があってもキャストができないので注意。
    RObjectにキャストすることはないと思いますが、、、念のため)