Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save niwatako/77cdca9f27569fc5498b2ff9ae6db87b to your computer and use it in GitHub Desktop.
Save niwatako/77cdca9f27569fc5498b2ff9ae6db87b to your computer and use it in GitHub Desktop.
"かつて存在しなかったRealmObjectクラス" のプロパティをリネームしたいときはどうしたら良いのか?についてまとめました。

前提

はじめにOctopusがいて...

public class Octopus: Object {
	public dynamic var legs: Int
}

途中からHumanが現れて、forefootsを持っている。

public class Octopus: Object {
	public dynamic var legs: Int
}

public class Human: Object {
	public dynamic var forefoots: Int
}

これからHumanのforefootsをarmsにリネームしたい。

public class Octopus: Object {
	public dynamic var legs: Int
}

public class Human: Object {
	public dynamic var arms: Int // Renamed to from "forefoots" in schemeVersion 2017
}

そこでマイグレーション処理( renameProperty(onType:from:) )を書きたいのですが、RealmがまだOctopusしかいなかった時代から突然やって来ると、そもそもHumanが存在しないので、 renameProperty(onType:from:) できません。

let migrationBlock: RLMMigrationBlock = { migration, oldSchemaVersion in
    /** ... */
    if oldSchemaVersion < 2017 {
        // Humanのforefootsをarmsにリネームする。
        // FIXME: アプリがHumanの居ない時代から来るとここで
        //        Cannot rename property 'Human.forefoots' because it does not exist."
        //        となってマイグレーションに失敗してしまいます。
        migration.renameProperty(onType: Human.className(), from: "forefoots", to: "arms")
    }
}

次のことが疑問です

  • HumanがいつのschemaVersionから存在していたのかを突き止め、renameProperty(onType:from:) を判断する条件文に追加すべきでしょうか?
    • schemaVersionで条件判定するのではなく、テーブル(ObjectSchema?)が存在=リネーム対象があるならば renameProperty(onType:from:) を行う、というような書き方は可能ですか?
  • schemaVersionで条件判定できればそれで安心ですか? : "Octopusの定義はずっと存在していたが、レコードを作ったことは一度も無かった"という時、Octopusに対して renameProperty(onType:from:) をしようとするとどうなってしまいますか?
    • Realmのテーブルが何をきっかけに生成されるのか理解していません。一度も保存したことがないモデルはテーブルが存在せず、バージョンで判別しても renameProperty(onType:from:) できない、ということは起き得ますか?
      • (もし起きるのであれば)基本的に renameProperty(onType:from:) の前には存在チェックが必要でしょうか?それは不要でしょうか?

このような方法を考えました

これでテーブルの存在を確認できそうですが、間違いないでしょうか。あるいは、もうすこし簡単な方法はありますか?

let shouldRename: Bool = migration.oldSchema.objectSchema.reduce(false) { result, scheme in
    return result || scheme.className == Human.className()
}

reduceよりfirst(where:)使ったほうが良さそうなのでコメントしてある。 https://gist.github.com/niwatako/77cdca9f27569fc5498b2ff9ae6db87b#gistcomment-2041941

きちんとschemaバージョンを管理しておくべき、とのこと。

  • Realm社の岸川さんにコメントでご回答を頂いた
  • 管理できていなかったら、しょうがないので上記の存在チェックをする方法でもよい。

つまりこういうのが理想

let migrationBlock: RLMMigrationBlock = { migration, oldSchemaVersion in
    /** ... */
    if oldSchemaVersion < 2017 {
        /** ... */
        // 2017へのマイグレーションならforefootsをリネームしたいけど、Humanが存在するのは1017以降なのでそのときだけリネーム
        if 1017 <= oldSchemaVersion {
	    migration.renameProperty(onType: Human.className(), from: "forefoots", to: "arms")
	}
    }
}
@niwatako
Copy link
Author

niwatako commented Mar 30, 2017

最後の方法でやるにしても、こうした方が良いかもしれない(些細)

if let _ = migration.oldSchema.objectSchema.first(where: { $0.className == Human.className() }) {
    migration.renameProperty(onType: Human.className(), from: "forefoots", to: "arms")
}

@kishikawakatsumi
Copy link

HumanがいつのschemaVersionから存在していたのかを突き止め、renameProperty(onType:from:) を判断する条件文に追加すべきでしょうか?

^ それが正しいやり方です。データベースのスキーマがいつから変わったのかわからないというのはとても危険な状態です。

schemaVersionで条件判定するのではなく、テーブル(ObjectSchema?)が存在=リネーム対象があるならば

わからないなら仕方ないので実行時にoldSchemaの状態を見て判断するしかないです。
テーブルがあるかどうかの判断は上記のコードどちらでもOKです。

schemaVersionで条件判定できればそれで安心ですか?
"Octopusの定義はずっと存在していたが、レコードを作ったことは一度も無かった"という時、Octopusに対して renameProperty(onType:from:) をしようとするとどうなってしまいますか?

データを作ったことがなかったとしてもテーブルの定義は存在するので成功します。新旧のスキーマが正しいなら成功します。

Realmのテーブルが何をきっかけに生成されるのか理解していません。一度も保存したことがないモデルはテーブルが存在せず、バージョンで判別しても renameProperty(onType:from:) できない、ということは起き得ますか?
(もし起きるのであれば)基本的に renameProperty(onType:from:) の前には存在チェックが必要でしょうか?それは不要でしょうか?

Realmを開いたときに、クラスファイルの検査が行われ、既存のファイルがなければ、定義された通りにテーブルが作られます。
データを保存したことがあるかないかにかかわらず、テーブルは存在します。
(例外として、過去のバージョンではプロパティを持たないRealmオブジェクトのサブクラスを作ることができました。その場合はテーブルは作られません。現在のバージョンではプロパティを1つも持たないクラスは作れなくなっています。)

renameProperty(onType:from:) の前には存在チェックが必要でしょうか?それは不要でしょうか?

不要です。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment