Realmでプライマリキーをauto incrementっぽく作る

  • 48
    Like
  • 0
    Comment
More than 1 year has passed since last update.

アプリ道場 Advent Calendar 2015の4日目は、こたら@kotala_bです。
Realmのプライマリキーの作り方について書いていきます。

プライマリキーって?

プライマリキーというのはOracleやMySqlといったリレーショナルデータベース(RDBMS)で使われている、一意性を保証するためのものです。
Realmでももちろんこのプライマリキーを設定できます。
Realmでプライマリキーを設定するにはこのようにします。

class Person: Object {
    dynamic var id: Int = 0
    dynamic var name: String = ""

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

RDBMSにはauto incrementと言って、プライマリキーに入れる一意性のある値を自動的に作ってくれる機能があるのですが、Realmにはありません。(v0.96.2時点)
なので、Realmではプライマリキーに入れる値を作る仕組みを自分で用意しなければなりません。

Realmのプライマリキーを作る

プライマリキーの作り方の1つとして「0から順に数値を採番し、保存されているプライマリキーの最大値に+1した値を新しいプライマリキーとして設定する」というような作り方が考えられます。

class MyRealm {

    var maxId: Int { return try! Realm().objects(Person).sorted("id").last?.id ?? 0 }

    func createNewPerson(name: String) {

        let person = Person()
        person.id = maxId + 1
        person.name = name

        let realm = try! Realm()
        try! realm.write{ realm.add(person) }
    }
}

上のコードでは、複数のスレッドで実行したときに正しく採番されない可能性があります。
それにmaxIdがふられてるデータを削除したら次に作ったデータにまた同じidがふられたりもするので、プライマリキーの採番を別のところで管理しないといけません。
ちょっと面倒くさそうです。

スレッドセーフなプライマリキーを作る

じゃあどうするの?ってことなんですけど、UUIDを使います。
UUIDというのは、

先人たちはUUIDという便利なものを用意してくれてCocoaにもしっかりと用意されているので、iOS、Objective-C、Swiftで識別子を作りたいときはこれを使いましょう
http://qiita.com/mo_to_44/items/9e41b6aba0c6898d373d

UUIDを都度生成すれば、複数スレッドで実行していても問題ありません。
プライマリキーにUUIDを使うとなると、連番ではなくなり、型もIntではなくなるのでそこだけは注意が必要です。
はじめのコードをUUIDを使ったカタチに書き直すとこうなります。

class Person: Object {
    dynamic var id: String = ""
    dynamic var name: String = ""

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

class MyRealm {    

    func createNewPerson(name: String) {

        let person = Person()
        person.id = NSUUID().UUIDString
        person.name = name

        let realm = try! Realm()
        try! realm.write{ realm.add(person) }
    }
}

var maxIdはもういらなくなりました。

auto incrementっぽくする

もう少しauto incrementっぽくするために、プロパティのデフォルト値にNSUUID().UUIDStringを設定するようにします。

class Person: Object {
    dynamic var id: String = NSUUID().UUIDString
    dynamic var name: String = ""

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

}

class MyRealm {

    func createNewPerson(name: String) {

        let person = Person()
        person.name = name

        let realm = try! Realm()
        try! realm.write{ realm.add(person) }
    }
}

これでだいぶauto incrementっぽくプライマリキーを作ってる感じがしてきました。

まとめ

今個人で作っているアプリの中でRealmを使っているんですが、プライマリキーはただ単にユニークな値であればよかったのでUUIDを使った方法を採用しました。

プライマリキーを連番で作るにはいろいろと心配事があって、少し面倒くさそうに感じました。
UUIDであれば特に細かいことを気にする必要はないかなと感じています。

今回試したみたコードはこちらにあります。
https://github.com/kotalab/RealmAutoIncrement

参考