プライマリキーを後からRealm Objectに追加してマイグレーションする際に、少し手こずったので備忘録します。
Realm Objectクラスにプライマリキーを追加
Before
class Diary: Object {
@objc dynamic var text: String = ""
@objc dynamic var createdAt: Date = Date()
}
After
class Diary: Object {
@objc dynamic var id: String = NSUUID().uuidString // primary key
@objc dynamic var text: String = ""
@objc dynamic var createdAt: Date = Date()
//Primary Keyの設定
override static func primaryKey() -> String? {
return "id"
}
}
追加した箇所
@objc dynamic var id: String = NSUUID().uuidString // primary key
Realmにはオートインクリメントの機能がないため、プライマリキーを連番の数字にしたい場合は自分で加算するロジックを実装する必要があります。今回は後からプライマリキーを追加するため、既存のデータに対してもidを振る必要があり処理を書くのが色々面倒だと感じました。
その手間を省くため、一意の文字列を生成してくれるUUIDをプライマリキーの初期値に指定しました。
もし作成順でデータが欲しくなった場合はcreatedAtカラムをソートすればいいため、Realmの場合はこの方法がいいかなと思いました。
//Primary Keyの設定
override static func primaryKey() -> String? {
return "id"
}
こちらの関数でプライマリキーのカラム名を返すことでRealm側がプライマリキーを認識します。
マイグレーション
ユーザーがアップデートした際に、DBの変更を正しく反映するためにマイグレーションが必要です。
起動時に通るAppDelegate.siwftのapplication~~関数で以下のように記述します。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
migration()
return true
}
// Realmマイグレーション処理
func migration() {
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if(oldSchemaVersion < 1) {
migration.enumerateObjects(ofType: Diary.className()) { oldObject, newObject in
newObject!["id"] = NSUUID().uuidString
}
}
}
)
}
RealmのSchema Versionを1へアップデートし既存のデータに対しプライマリキーのUUIDを追加しています。(初期のRealmのSchema Versionは0)
if(oldSchemaVersion < 1) {
migration.enumerateObjects(ofType: Diary.className()) { oldObject, newObject in
newObject!["id"] = NSUUID().uuidString
}
}
このif文について
他のサイトでは、カラムの追加と削除をする場合はこちらのifの中身は何も書く必要がなく自動でRealm側が認識してくれると記載がありましたが、僕の環境ではエラーが出ました。
既存データに対しidの初期値を与えることでエラーが消えたため、おそらくプライマリキーを設定したい場合はこのように記述する必要がありそうです。