これをせずに、モデル変更 → エラーかます → ググる を繰り返してるので、覚えるためにもまとめます。w
マイグレーションとは
モデル定義が変わったときに、既存データを新規データの仕様に移行することです。
公式ドキュメントの例を少し拝借すると、例えば、以下のようなユーザー情報を保持するモデルクラスがあるとします。
//----- Ver1.0.0 -----//
class User: Object {
@objc dynamic var uniqueId = ""
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var age = 0
override static func primaryKey() -> String {
return "uniqueId"
}
}
次のアップデートで「名前を1つに統一する」仕様になったとします。
そうすると、
//----- Ver1.0.1(姓名を統一する場合) -----//
class User: Object {
@objc dynamic var uniqueId = ""
@objc dynamic var fullName = ""
@objc dynamic var age = 0
override static func primaryKey() -> String {
return "uniqueId"
}
}
こんな感じになります。
この fullName は、firstName + lastName となり、既存データ(Ver1.0.0)を新規データ(Ver1.0.1)の仕様に移行する必要があるので、マイグレーションが必要になります!
ちなみに、この「マイグレーション」という用語自体は Apple や Realm 特有のものではなく、一般的なデータベース用語です。
データベースとか特に詳しくなかったので、最初は特有のものと思ってました( ゚д゚ )
実装
先ほどのユーザーを例に実装していきます。
マイグレーションは結構簡単で、AppDelegate に以下のようなコードを挿入したら完了です。
【2020年7月20日】最新コードを掲載。
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions: launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
var config = Realm.Configuration()
config.migrationBlock = { migration, oldSchemaVersion in
// 設定していなければ oldSchemaVersion はゼロがデフォルトです
if oldSchemaVersion < 1 {
// 保存されている User 全てを列挙
migration.enumerateObjects(ofType: User.className()) { (oldObject, newObject) in
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = firstName + lastName
}
}
}
// 現在のRealmファイルの schemaVersion と、下記で設定した schemaVersion が違うと、マイグレーションが実行される
config.schemaVersion = 1
Realm.Configuration.defaultConfiguration = config
return true
}
基本的にやってることは変わりませんが、すっきりした印象です。
以前のコードも残しておきます!
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions: launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = Realm.Configuration(schemaVersion: 1, migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
migration.enumerateObjects(ofType: User.className()) { oldObject, newObject in
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = firstName + lastName
}
}
})
Realm.Configuration.defaultConfiguration = config
return true
}
}
データ構造を変えるたびに、schemaVersion をインクリメントしていけば大丈夫です。
注意する点
このマイグレーション処理で気をつけたいのが、アプリのアップデートを飛ばすユーザーもいるということです。
Ver1.0.0 をインストールしているユーザーが、Ver1.0.1 を飛ばして Ver1.0.2 にアップデートするかもしれません。
なので、マイグレーション処理はネストせず、比較も「 < 」で行うことです。
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions: launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
var config = Realm.Configuration()
config.migrationBlock = { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
migration.enumerateObjects(ofType: User.className()) { (oldObject, newObject) in
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = firstName + lastName
}
}
if oldSchemaVersion < 2 {
// ここで次のマイグレーションを記載する
}
})
config.schemaVersion = 1
Realm.Configuration.defaultConfiguration = config
return true
}
}
ここ失敗するとユーザーが離れる事態になりかねないので、慎重に作業していきましょう (·ω·`*)
br>
br>
違うよ!とかこういう方法あるよ!ってのがあれば教えてください。
では( ¯·ω·¯ )



コメント