Docs Menu
Docs Home
/ /
Datos del modelo

Cambiar un modelo de objeto - Swift SDK

Nota

Modificar las propiedades del esquema de un dominio sincronizado

La siguiente página muestra cómo modificar las propiedades del esquema de un dominio local. Aprenda a... modificar las propiedades del esquema de un reino sincronizado.

Cuando actualiza su esquema de objetos, debe incrementar la versión del esquema y realizar una migración.

Tip

Esta página proporciona ejemplos generales de migración de Swift y Objective-C. Si utiliza Realm con SwiftUI, consulte los ejemplos de migración específicos de SwiftUI.

Si tu actualización de esquema añade propiedades opcionales o remueve propiedades, Realm puede realizar la migración automáticamente. Solo necesitas incrementar el schemaVersion.

Para actualizaciones de esquema más complejas, también debe especificar manualmente la lógica de migración en un migrationBlock. Esto podría incluir cambios como:

  • Agregar propiedades obligatorias que deben completarse con valores predeterminados

  • Combinando campos

  • Cambiar el nombre de un campo

  • Cambiar el tipo de un campo

  • Conversión de un objeto a un objeto incrustado

Tip

Evitar la migración durante el desarrollo

Al desarrollar o depurar tu aplicación, puede preferirse borrar el realm en lugar de migrarlo. Utiliza la bandera deleteRealmIfMigrationNeeded para borrar la base de datos automáticamente cuando un desajuste de esquema requiera una migración.

Nunca publique una aplicación en producción con esta marca configurada en true.

Una versión de esquema identifica el estado de un esquema de dominio en un momento dado. Dominio rastrea la versión de esquema de cada dominio y la utiliza para asignar los objetos de cada dominio al esquema correcto.

Las versiones del esquema son números enteros que se pueden incluir en la configuración del dominio al abrirlo. Si una aplicación cliente no especifica un número de versión al abrir un dominio, este se establece como 0 de forma predeterminada.

Importante

Incrementar versiones monótonamente

Las migraciones deben actualizar un dominio a una versión de esquema superior. El dominio generará un error si una aplicación cliente abre un dominio con una versión de esquema inferior a la actual, o si la versión de esquema especificada coincide con la actual, pero incluye esquemas de objetos diferentes.

Una migración local es la migración de un dominio que no se sincroniza automáticamente con otro. Las migraciones locales tienen acceso al esquema, la versión y los objetos del dominio existentes, y definen la lógica que actualiza el dominio gradualmente a su nueva versión del esquema. Para realizar una migración local, debe especificar una nueva versión del esquema superior a la actual y proporcionar una función de migración al abrir el dominio obsoleto.

En iOS, puedes actualizar los datos subyacentes para reflejar los cambios en el esquema usando migraciones manuales. Durante una migración manual, puedes definir nuevas propiedades y propiedades eliminadas cuando se añaden o eliminan del esquema.

Realm puede migrar automáticamente las propiedades agregadas, pero debe especificar una versión de esquema actualizada cuando realice estos cambios.

Nota

Realm no establece automáticamente los valores de las nuevas propiedades obligatorias. Debe usar un bloque de migración para establecer los valores predeterminados de las nuevas propiedades obligatorias. Para las nuevas propiedades opcionales, los registros existentes pueden tener valores nulos. Esto significa que no necesita un bloque de migración al agregar propiedades opcionales.

Ejemplo

Un reino que utiliza la versión de esquema 1 tiene un tipo de objeto Person que tiene propiedades de nombre, apellido y edad:

// 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
@Persisted var age = 0
}

El desarrollador decide que la clase Person necesita un campo email y actualiza el 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
// Add a new "email" property.
@Persisted var email: String?
// New properties can be migrated
// automatically, but must update the schema version.
@Persisted var age = 0
}

Realm migra automáticamente el realm para ajustarse al esquema de Person actualizado. Pero el desarrollador debe definir la versión del esquema del 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()

Para eliminar una propiedad de un esquema, elimínela de la clase del objeto y establezca un valor schemaVersion en el objeto de configuración del dominio. Eliminar una propiedad no afecta a los objetos existentes.

Ejemplo

Un reino que utiliza la versión de esquema 1 tiene un tipo de objeto Person que tiene propiedades de nombre, apellido y edad:

// 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
@Persisted var age = 0
}

El desarrollador decide que el Person no necesita el campo age y actualiza el 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
// Remove the "age" property.
// @Persisted var age = 0
// Removed properties can be migrated
// automatically, but must update the schema version.
}

Realm migra automáticamente el realm para ajustarse al esquema de Person actualizado. Pero el desarrollador debe definir la versión del esquema del 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()

Tip

Los desarrolladores de SwiftUI pueden ver un error que indica que se requiere una migración al agregar o eliminar propiedades. Esto está relacionado con el ciclo de vida de SwiftUI. Las vistas se diseñan y luego el modificador .environment establece la configuración.

Para resolver un error de migración en estas circunstancias, pase Realm.Configuration(schemaVersion: <Your Incremented Version>) al constructor ObservedResults.

Para actualizaciones de esquema más complejas, Realm requiere una migración manual de instancias antiguas de un objeto determinado al nuevo esquema.

Para cambiar el nombre de una propiedad durante una migración, utilice el método Migration.renameProperty(onType:from:to:).

Realm aplica cualquier nueva configuración de nulidad o indexación durante la operación de cambio de nombre.

Ejemplo

Cambie age el nombre de a yearsSinceBirth dentro de un bloque de migración.

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

Puedes usar el método deleteRealmIfMigrationNeeded para eliminar el dominio si requiere una migración. Esto puede ser útil durante el desarrollo cuando necesitas iterar rápidamente y no quieres realizar la migración.

Para definir una lógica de migración personalizada,configure la propiedad migrationBlock de la Configuración al abrir un reino.

El bloque de migración recibe un objeto Migration que puede usar para realizar la migración. Puede usar el método enumerateObjects(ofType:_:) del objeto Migration para iterar y actualizar todas las instancias de un tipo de dominio determinado en el dominio.

Ejemplo

Un reino que utiliza la versión de esquema 1 tiene un tipo de objeto Person que tiene campos separados para nombre y apellido:

// 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
@Persisted var age = 0
}

El desarrollador decide que la clase Person debe utilizar un campo combinado fullName en lugar de los campos separados firstName y lastName y actualiza el esquema.

Para migrar el reino para que se ajuste al esquema Person actualizado, el desarrollador establece la versión del esquema del reino en 2 y define una función de migración para establecer el valor de fullName en función de las propiedades firstName y 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 {
@Persisted var fullName = ""
@Persisted 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()

Más tarde, el desarrollador decide que el campo age debe ser de tipo String en lugar de Int y actualiza el esquema.

Para migrar el reino para que se ajuste al esquema Person actualizado, el desarrollador establece la versión del esquema del reino en 3 y agrega una condición a la función de migración para que la función defina cómo migrar desde cualquier versión anterior a la nueva.

// 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 {
@Persisted var fullName = ""
@Persisted 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

Migraciones lineales

Evite anidar u omitir las sentencias if (oldSchemaVersion < X) en los bloques de migración. Esto garantiza que todas las actualizaciones se apliquen en el orden correcto, independientemente de la versión del esquema desde la que comience el cliente. El objetivo es definir una lógica de migración que pueda transformar los datos de cualquier versión de esquema obsoleta para que coincidan con el esquema actual.

Los objetos incrustados no pueden existir independientemente de un objeto principal. Al convertir un objeto en un objeto incrustado, el bloque de migración debe garantizar que cada objeto incrustado tenga exactamente un enlace a un objeto principal. La ausencia de enlaces o la existencia de varios genera las siguientes excepciones:

At least one object does not have a backlink (data would get lost).
At least one object does have multiple backlinks.

Consulte los ejemplos de migración adicionales en el repositorio realm-swift.

Volver

Tipos admitidos

En esta página