Alterar um modelo de objeto - Swift SDK
Nesta página
Observação
Modificar Propriedades do Esquema de um Domínio Sincronizado
A página seguinte demonstra como modificar as propriedades do esquema de um Realm local. Saiba como modificar as propriedades do esquema de um Realm sincronizado.
Visão geral
Ao atualizar o esquema de objetos, você deve incrementar a versão do esquema e executar uma migração.
Dica
Veja também:
Esta página fornece exemplos gerais de migração para Swift e Objective-C. Se você estiver usando o Realm com o SwiftUI, consulte os exemplos de migração específicos do SwiftUI.
Se a atualização do esquema adicionar propriedades opcionais ou remover propriedades, o Realm poderá executar a migração automaticamente. Basta incrementar o schemaVersion
.
Para atualizações de esquema mais complexas, você também deve especificar manualmente a lógica de migração em um migrationBlock
. Isso pode incluir alterações como:
Adicionando propriedades necessárias que devem ser preenchidas com valores padrão
Combinando campos
Renomeando um campo
Como alterar o tipo de campo
Convertendo de um objeto para um objeto incorporado
Dica
Ignorar migração durante o desenvolvimento
Ao desenvolver ou depurar seu aplicativo, talvez você prefira excluir o Realm em vez de migrá-lo. Use o sinalizador deleteRealmifMigrationNeeded para excluir o banco de dados automaticamente quando uma incompatibilidade de esquema exigir uma migração.
Nunca libere um aplicativo para produção com esta sinalização definida como true
.
Versão do esquema
Uma versão de esquema identifica o estado de um Realm Schema em um determinado momento. O Realm controla a versão do esquema de cada realm e o usa para mapear os objetos em cada realm para o esquema correto.
As versões de esquema são inteiros que você pode incluir na configuração do território quando você abre um domínio. Se um aplicativo cliente não especificar um número de versão quando abrir um domínio, o domínio assumirá como padrão a versão 0
.
Importante
Incrementar versões monotonicamente
As migrações devem atualizar um realm para uma versão superior do esquema. O Realm irá gerar um erro se um aplicativo cliente abrir um realm com uma versão do esquema inferior à versão atual do realm ou se a versão do esquema especificada for igual à versão atual do realm, mas incluir esquemas de objetos diferentes.
Migrações
Uma migração local é uma migração para um domínio que não é sincronizado automaticamente com outro domínio. As migrações locais têm acesso ao domínio schema, à versão e aos objetos existentes e definem a lógica que atualiza incrementalmente o domínio para sua nova versão do esquema. Para realizar uma migração local, você precisa especificar uma nova versão do esquema que seja superior à versão atual e fornecer uma função de migração ao abrir o domínio desatualizado.
No iOS, você pode atualizar os dados subjacentes para refletir as alterações de esquema usando migrações manuais. Durante essa migração manual, você pode definir propriedades novas e excluídas quando elas são adicionadas ou removidas do esquema.
Atualize esquemas automaticamente
Adicionar uma propriedade
O Realm pode migrar automaticamente as propriedades adicionadas, mas você deve especificar uma versão atualizada do esquema ao fazer essas mudanças.
Observação
O Realm não define valores automaticamente para novas propriedades obrigatórias. Você deve usar um bloco de migração para configurar valores padrão para novas propriedades obrigatórias. Para novas propriedades opcionais, os registros existentes podem ter valores nulos. Isso significa que você não precisa de um bloco de migração ao adicionar propriedades opcionais.
Exemplo
Um Realm que utiliza a versão 1
do esquema tem um tipo de objeto Person
com propriedades de nome, sobrenome e idade:
// 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 }
O desenvolvedor decide que a classe Person
precisa de um campo email
e atualiza o esquema.
// 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 }
O Realm migra automaticamente o domínio para estar em conformidade com o esquema Person
atualizado. Mas o desenvolvedor deve definir a versão do esquema do Realm como 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()
Excluir uma propriedade
Para excluir uma propriedade de um esquema, remova a propriedade da classe do objeto e defina um schemaVersion
do objeto de configuração do Realm. Excluir uma propriedade não afetará os objetos existentes.
Exemplo
Um Realm que utiliza a versão 1
do esquema tem um tipo de objeto Person
com propriedades de nome, sobrenome e idade:
// 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 }
O desenvolvedor decide que Person
não precisa do campo age
e atualiza o esquema.
// 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. }
O Realm migra automaticamente o domínio para estar em conformidade com o esquema Person
atualizado. Mas o desenvolvedor deve definir a versão do esquema do Realm como 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()
Dica
Os desenvolvedores do SwiftUI podem ver um erro informando que uma migração é necessária ao adicionar ou excluir propriedades. Isso está relacionado ao ciclo de vida do SwiftUI. As visualizações são dispostas e, em seguida, o modificador .environment
define a configuração.
Para resolver um erro de migração nessas circunstâncias, passe Realm.Configuration(schemaVersion: <Your Incremented Version>)
para o construtor ObservedResults
.
Migre esquemas manualmente
Para atualizações de esquema mais complexas, o Realm exige uma migração manual para instâncias antigas de um determinado objeto para o novo esquema.
Renomear uma propriedade
Para renomear uma propriedade durante uma migração, use Migration.renameProperty(onType:from:to:) como método.
O Realm aplica quaisquer novas configurações de nulidade ou indexação durante a operação de renomeação.
Exemplo
Renomeie age
para yearsSinceBirth
em um MigrationBlock.
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") } })
Modifique propriedades
Dica
Você pode usar o método deleteRealmIfMigrationNeeded para excluir o realm se ele exigir uma migração. Isso pode ser útil durante o desenvolvimento quando você precisar iterar rapidamente e não quiser fazer a migração.
Para definir a lógica de migração personalizada, defina a propriedade migrationBlock da configuração ao abrir um Realm.
O bloco de migração recebe um objeto de migração que você pode usar para realizar a migração. Você pode usar o método enumerateObjects(ofType:_:) do objeto de migração para iterar e atualizar todas as instâncias de um determinado tipo de Realm no Realm.
Exemplo
Um Realm que usa a versão 1
do esquema tem um tipo de objeto Person
que tem campos separados para nomes e sobrenomes:
// 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 }
O desenvolvedor decide que a classe Person
deve utilizar um campo fullName
combinado em vez dos campos firstName
e lastName
separados e atualiza o esquema.
Para migrar o Realm para estar em conformidade com o esquema Person
atualizado, o desenvolvedor define a versão do esquema do Realm como 2
e define uma função de migração para configurar o valor de fullName
com base nas propriedades firstName
e lastName
existentes.
// 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()
Mais tarde, o desenvolvedor decide que o campo age
deve ser do tipo String
em vez de Int
e atualiza o esquema.
Para migrar o Realm para estar em conformidade com o esquema Person
atualizado, o desenvolvedor define a versão do esquema do Realm como 3
e adiciona uma condicional à função de migração para que a função defina como migrar de qualquer versão anterior para a mais recente.
// 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()
Dica
Migrações lineares
Evite aninhar ou ignorar as instruções if (oldSchemaVersion < X)
nos blocos de migração. Isso garante que todas as atualizações possam ser aplicadas na ordem correta, independentemente da versão do esquema a partir da qual um cliente é iniciado. O objetivo é definir a lógica de migração que pode transformar os dados de qualquer versão desatualizada do esquema para que correspondam ao esquema atual.
Converta de objeto para objeto integrado
Objetos incorporados não podem existir independentemente de um objeto pai. Ao alterar um objeto para um objeto incorporado, o bloco de migração deve garantir que cada objeto incorporado tenha exatamente um backlink para um objeto pai. Não ter backlinks ou ter vários backlinks gera as seguintes exceções:
At least one object does not have a backlink (data would get lost).
At least one object does have multiple backlinks.
Exemplos adicionais de migração
Confira os exemplos de migração adicionais no repositório realm-swift.