オブジェクトモデルの変更 - Swift SDK
項目一覧
注意
同期された Realm のスキーマ プロパティの変更
次のページでは、ローカル Realm のスキーマ プロパティを変更する方法を示します。 同期された Realm のスキーマ プロパティを変更する方法を学びます。
Overview
オブジェクト スキーマを更新するときは、スキーマのバージョンを増やして移行を実行する必要があります。
Tip
以下も参照してください。
このページでは、一般的な Swift と Objective-C の移行例を取り上げます。 SwiftUI で Realm を使用している場合は、 SwiftUI 固有の移行例 を参照してください。
スキーマ更新によってオプションのプロパティが追加されるか、プロパティが削除される場合、Realm は移行を自動的に実行できます。 schemaVersion
を増やす必要があるだけです。
より複雑なスキーマ更新では、 migrationBlock
で移行ロジックを手動で指定する必要もあります。 これには、次のような変更が含まれる場合があります。
デフォルト値を入力する必要がある必須プロパティの追加
フィールドの組み合わせ
フィールドの名前を変更する
フィールドの型の変更
オブジェクトから埋め込みオブジェクトへの変換
Tip
開発中のバイパス移行
アプリケーションを開発またはデバッグする際には、Realm を移行するのではなく、Realm を削除することをお勧めします。 スキーマの不一致により移行が必要になる場合に、データベースを自動的に削除するには、 deleteRealmIfMigrationNetedフラグを使用します。
このフラグをtrue
に設定して本番環境にアプリをリリース しないでください 。
スキーマ バージョン
スキーマ バージョンは、ある時点におけるRealm スキーマの状態を識別します。 Realm は各 Realm のスキーマ バージョンを追跡し、それを使用して各 Realm 内のオブジェクトを正しいスキーマにマッピングします。
スキーマ バージョンは、Realm を開くときに Realm 構成に含めることができる整数です。 クライアント アプリケーションが Realm を開くときにバージョン番号を指定しない場合、Realm はデフォルトでバージョン0
になります。
重要
バージョンを単調に増加させる
移行によって、Realm をより高いスキーマ バージョンに更新する必要があります。 Realm は、Realm の現在のバージョンよりも低いスキーマ バージョンでクライアント アプリケーションが Realm を開き、または指定されたスキーマ バージョンが Realm の現在のバージョンと同じであるが、異なるオブジェクト スキーマを含む場合にエラーをスローします。
移行
ローカル移行とは、別の Realm と自動的に同期しない Realm の移行です。 ローカル移行では、既存の Realm スキーマ、バージョン、およびオブジェクトにアクセスでき、Realm を新しいスキーマ バージョンに段階的に更新するロジックを定義します。 ローカル移行を実行するには、現在のバージョンよりも高い新しいスキーマ バージョンを指定し、古くなった Realm を開くときに移行機能を提供する必要があります。
iOS では、手動移行を使用してスキーマの変更を反映するように基礎となるデータを更新できます。 このような手動移行中に、スキーマに追加または削除されるときに、新しいプロパティと削除されたプロパティを定義できます。
スキーマの自動更新
プロパティを追加する
Realm は追加されたプロパティを自動的に移行できますが、これらを変更するときに更新されたスキーマ バージョンを指定する必要があります。
注意
Realm は、新しい必須プロパティの値を自動的に設定しません。 新しい必須プロパティにデフォルト値を設定するには、移行ブロックを使用する必要があります。 新しい任意プロパティの場合、既存のレコードは null 値を持つことができます。 つまり、オプションのプロパティを追加するときに移行ブロックは必要ありません。
例
スキーマ バージョン1
を使用する Realm には、名、姓、および年数のプロパティを持つPerson
オブジェクトタイプがあります。
// In the first version of the app, the Person model // has separate fields for first and last names, // and an age property. @interface Person : RLMObject @property NSString *firstName; @property NSString *lastName; @property int age; @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"firstName", @"lastName", @"age"]; } @end
// In the first version of the app, the Person model // has separate fields for first and last names, // and an age property. class Person: Object { var firstName = "" var lastName = "" var age = 0 }
開発者は、 Person
クラスにemail
フィールドが必要であると判断し、スキーマを更新します。
// In a new version, you add a property // on the Person model. @interface Person : RLMObject @property NSString *firstName; @property NSString *lastName; // Add a new "email" property. @property NSString *email; // New properties can be migrated // automatically, but must update the schema version. @property int age; @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"firstName", @"lastName", @"email", @"age"]; } @end
// In a new version, you add a property // on the Person model. class Person: Object { var firstName = "" var lastName = "" // Add a new "email" property. var email: String? // New properties can be migrated // automatically, but must update the schema version. var age = 0 }
Realm は、更新されたPerson
スキーマに準拠するように Realm を自動的に移行します。 ただし、開発者は Realm のスキーマ バージョンを2
に設定する必要があります。
// When you open the realm, specify that the schema // is now using a newer version. RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; // Set the new schema version config.schemaVersion = 2; // Use this configuration when opening realms [RLMRealmConfiguration setDefaultConfiguration:config]; RLMRealm *realm = [RLMRealm defaultRealm];
// When you open the realm, specify that the schema // is now using a newer version. let config = Realm.Configuration( schemaVersion: 2) // Use this configuration when opening realms Realm.Configuration.defaultConfiguration = config let realm = try! Realm()
プロパティを削除する
スキーマからプロパティを削除するには、オブジェクトのクラスからプロパティを削除し、Realm の構成オブジェクトのschemaVersion
を設定します。 Deleting a property will not impact existing objects.
例
スキーマ バージョン1
を使用する Realm には、名、姓、および年数のプロパティを持つPerson
オブジェクトタイプがあります。
// In the first version of the app, the Person model // has separate fields for first and last names, // and an age property. @interface Person : RLMObject @property NSString *firstName; @property NSString *lastName; @property int age; @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"firstName", @"lastName", @"age"]; } @end
// In the first version of the app, the Person model // has separate fields for first and last names, // and an age property. class Person: Object { var firstName = "" var lastName = "" var age = 0 }
開発者は、 Person
にはage
フィールドが不要と判断され、スキーマを更新します。
// In a new version, you remove a property // on the Person model. @interface Person : RLMObject @property NSString *firstName; @property NSString *lastName; // Remove the "age" property. // @property int age; // Removed properties can be migrated // automatically, but must update the schema version. @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"firstName", @"lastName"]; } @end
// In a new version, you remove a property // on the Person model. class Person: Object { var firstName = "" var lastName = "" // Remove the "age" property. // @Persisted var age = 0 // Removed properties can be migrated // automatically, but must update the schema version. }
Realm は、更新されたPerson
スキーマに準拠するように Realm を自動的に移行します。 ただし、開発者は Realm のスキーマ バージョンを2
に設定する必要があります。
// When you open the realm, specify that the schema // is now using a newer version. RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; // Set the new schema version config.schemaVersion = 2; // Use this configuration when opening realms [RLMRealmConfiguration setDefaultConfiguration:config]; RLMRealm *realm = [RLMRealm defaultRealm];
// When you open the realm, specify that the schema // is now using a newer version. let config = Realm.Configuration( schemaVersion: 2) // Use this configuration when opening realms Realm.Configuration.defaultConfiguration = config let realm = try! Realm()
Tip
SwiftUI 開発者は、プロパティを追加または削除するときに移行が必要であるというエラーが表示される場合があります。 これは、SwiftUI のライフサイクルに関連しています。 ビューは配置され、 .environment
修飾子によって構成が設定されます。
このような状況で移行エラーを解決するには、 Realm.Configuration(schemaVersion: <Your Incremented Version>)
をObservedResults
コンストラクターに渡します。
スキーマの手動移行
より複雑なスキーマ更新の場合、Realm では特定のオブジェクトの古いインスタンスを新しいスキーマに手動で移行する必要があります。
プロパティの名前を変更する
移行中にプロパティの名前を変更するには、 Migration.renameProperty(onType:from:to:) メソッドを使用します 使用して複数のドキュメントを挿入できます。
Realm は、名前変更操作中に新しい null 可能性またはインデックス作成設定を適用します。
例
migrationBlock内でage
の名前をyearsSinceBirth
に変更します。
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; config.schemaVersion = 2; config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion < 2) { // Rename the "age" property to "yearsSinceBirth". // The renaming operation should be done outside of calls to `enumerateObjects(ofType: _:)`. [migration renamePropertyForClass:[Person className] oldName:@"age" newName:@"yearsSinceBirth"]; } };
let config = Realm.Configuration( schemaVersion: 2, migrationBlock: { migration, oldSchemaVersion in if oldSchemaVersion < 2 { // Rename the "age" property to "yearsSinceBirth". // The renaming operation should be done outside of calls to `enumerateObjects(ofType: _:)`. migration.renameProperty(onType: Person.className(), from: "age", to: "yearsSinceBirth") } })
プロパティを変更
Tip
移行が必要な場合は、 deleteRealmIfMigrationNetedメソッドを使用してRealmを削除できます。 これは、開発中に迅速に反復処理する必要があり、移行を実行したくない場合に役立ちます。
カスタム移行ロジックを定義するには、Realm を 開く ときに 構成 の migrationBlock プロパティを設定します。
移行ブロックには、移行を実行するために使用できる移行オブジェクトが与えられます。 移行オブジェクトのenumObjects( ofType:_:)メソッドを使用して、Realm 内の特定の Realm タイプのすべてのインスタンスを反復処理し、更新できます。
例
スキーマ バージョン1
を使用する Realm には、氏名用に別々のフィールドを持つPerson
オブジェクトタイプがあります。
// In the first version of the app, the Person model // has separate fields for first and last names, // and an age property. @interface Person : RLMObject @property NSString *firstName; @property NSString *lastName; @property int age; @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"firstName", @"lastName", @"age"]; } @end
// In the first version of the app, the Person model // has separate fields for first and last names, // and an age property. class Person: Object { var firstName = "" var lastName = "" var age = 0 }
開発者は、 Person
クラスで個別のfirstName
フィールドとlastName
フィールドではなく、結合されたfullName
フィールドを使用する必要があると判断し、スキーマを更新します。
更新された スキーマに準拠するように RealmPerson
を移行するには、開発者は Realm のスキーマ バージョンを2
fullName
に設定し、既存のfirstName
プロパティとlastName
プロパティに基づいて の値を設定するための移行関数を定義します。 。
// In version 2, the Person model has one // combined field for the full name and age as a Int. // A manual migration will be required to convert from // version 1 to this version. @interface Person : RLMObject @property NSString *fullName; @property int age; @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"fullName", @"age"]; } @end
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; // Set the new schema version config.schemaVersion = 2; config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion < 2) { // Iterate over every 'Person' object stored in the Realm file to // apply the migration [migration enumerateObjects:[Person className] block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { // Combine name fields into a single field newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]]; }]; } }; // Tell Realm to use this new configuration object for the default Realm [RLMRealmConfiguration setDefaultConfiguration:config]; // Now that we've told Realm how to handle the schema change, opening the realm // will automatically perform the migration RLMRealm *realm = [RLMRealm defaultRealm];
// In version 2, the Person model has one // combined field for the full name and age as a Int. // A manual migration will be required to convert from // version 1 to this version. class Person: Object { var fullName = "" var age = 0 }
// In application(_:didFinishLaunchingWithOptions:) let config = Realm.Configuration( schemaVersion: 2, // Set the new schema version. migrationBlock: { migration, oldSchemaVersion in if oldSchemaVersion < 2 { // The enumerateObjects(ofType:_:) method iterates over // every Person object stored in the Realm file to apply the migration migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in // combine name fields into a single field let firstName = oldObject!["firstName"] as? String let lastName = oldObject!["lastName"] as? String newObject!["fullName"] = "\(firstName!) \(lastName!)" } } } ) // Tell Realm to use this new configuration object for the default Realm Realm.Configuration.defaultConfiguration = config // Now that we've told Realm how to handle the schema change, opening the file // will automatically perform the migration let realm = try! Realm()
その後、開発者はage
フィールドが ではなくString
Int
であると判断し、スキーマを更新します。
更新されたPerson
スキーマに準拠するように Realm を移行するには、開発者は Realm のスキーマ バージョンを3
に設定し、移行関数に条件付きを追加して、以前のバージョンから新しいバージョンに移行する方法を定義します。
// In version 3, the Person model has one // combined field for the full name and age as a String. // A manual migration will be required to convert from // version 2 to this version. @interface Person : RLMObject @property NSString *fullName; @property NSString *age; @end @implementation Person + (NSArray<NSString *> *)requiredProperties { return @[@"fullName", @"age"]; } @end
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; // Set the new schema version config.schemaVersion = 3; config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion < 2) { // Previous Migration. [migration enumerateObjects:[Person className] block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]]; }]; } if (oldSchemaVersion < 3) { // New Migration [migration enumerateObjects:[Person className] block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { // Make age a String instead of an Int newObject[@"age"] = [oldObject[@"age"] stringValue]; }]; } }; // Tell Realm to use this new configuration object for the default Realm [RLMRealmConfiguration setDefaultConfiguration:config]; // Now that we've told Realm how to handle the schema change, opening the realm // will automatically perform the migration RLMRealm *realm = [RLMRealm defaultRealm];
// In version 3, the Person model has one // combined field for the full name and age as a String. // A manual migration will be required to convert from // version 2 to this version. class Person: Object { var fullName = "" var age = "0" }
// In application(_:didFinishLaunchingWithOptions:) let config = Realm.Configuration( schemaVersion: 3, // Set the new schema version. migrationBlock: { migration, oldSchemaVersion in if oldSchemaVersion < 2 { // Previous Migration. migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in let firstName = oldObject!["firstName"] as? String let lastName = oldObject!["lastName"] as? String newObject!["fullName"] = "\(firstName!) \(lastName!)" } } if oldSchemaVersion < 3 { // New Migration. migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in // Make age a String instead of an Int newObject!["age"] = "\(oldObject!["age"] ?? 0)" } } } ) // Tell Realm to use this new configuration object for the default Realm Realm.Configuration.defaultConfiguration = config // Now that we've told Realm how to handle the schema change, opening the file // will automatically perform the migration let realm = try! Realm()
Tip
順次移行
移行ブロックでif (oldSchemaVersion < X)
ステートメントをネストしたりスキップしたりしないでください。 これにより、クライアントがどのスキーマ バージョンから開始するかに関係なく、すべての更新を正しい順序で適用できるようになります。 目的は、古いスキーマ バージョンのデータを現在のスキーマと一致するように変換できる移行ロジックを定義することです。
オブジェクトから埋め込みオブジェクトへの変換
埋め込みオブジェクトは、親オブジェクトと独立して存在することはできません。 オブジェクトを 埋め込みオブジェクトに変更する場合、移行ブロックは、すべての埋め込みオブジェクトが親オブジェクトへのバックリンクを 1 つだけ確保する必要があります。 バックリンクが存在しない場合、または複数のバックリンクがある場合、次の例外が発生します。
At least one object does not have a backlink (data would get lost).
At least one object does have multiple backlinks.
その他の移行例
Realm-swift リポジトリ でその他の移行例を確認してください。