アプリ道場 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