Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

객체 모델 변경 - Swift SDK

이 페이지의 내용

  • 개요
  • 스키마 버전
  • 마이그레이션
  • 스키마 자동 업데이트
  • 속성 추가
  • 속성 삭제
  • 스키마 수동 마이그레이션
  • 속성 이름 바꾸기
  • 속성 수정
  • 객체에서 임베디드 객체로 변환
  • 추가 마이그레이션 예시

참고

동기화된 Realm의 스키마 속성 수정

다음 페이지에서는 로컬 영역 의 스키마 속성을 수정하는 방법을 설명합니다. 동기화된 영역 의 스키마 속성을 수정하는 방법을 알아보세요.

객체 스키마를 업데이트할 때 스키마의 버전을 높이고 마이그레이션을 수행해야 합니다.

다음도 참조하세요.

이 페이지에서는 일반적인 Swift 및 오브젝티브-C 마이그레이션 예시를 제공합니다. SwiftUI와 함께 Realm을 사용하는 경우 SwiftUI 관련 마이그레이션 예시를 참조하세요.

스키마 업데이트가 선택적 속성을 추가하거나 제거하면 Realm은 자동으로 마이그레이션을 수행할 수 있습니다. schemaVersion만 증분하면 됩니다.

좀 더 복잡한 스키마 업데이트는 migrationBlock에서 마이그레이션 논리를 수동으로 지정해야 합니다. 여기에는 다음과 같은 변경 사항이 포함될 수 있습니다.

  • 기본값으로 채워야 하는 필수 속성 추가

  • 필드 결합

  • 필드 이름 바꾸기

  • 필드 유형 변경

  • 객체에서 내장된 객체로 변환

개발 중 마이그레이션 우회

애플리케이션 을 개발하거나 디버깅할 때 영역 을 마이그레이션하는 대신 삭제 하는 것이 나을 수 있습니다. 스키마 불일치로 인해 마이그레이션 이 필요한 경우 deleteRealmIfMigrationNeeded 플래그를 사용하여 데이터베이스 를 자동으로 삭제 합니다.

이 플래그를 true로 설정한 상태에서는 앱을 프로덕션에 배포하지 마세요.

스키마 버전은 특정 시점의 Realm 스키마 상태를 식별합니다. Realm은 각 realm의 스키마 버전을 추적하고 이를 사용하여 각 realm의 객체를 올바른 스키마에 매핑합니다.

스키마 버전은 realm을 열 때 realm 구성에 포함할 수 있는 정수입니다. 클라이언트 애플리케이션이 realm을 열 때 버전을 지정하지 않으면 realm의 기본값은 버전 0 입니다.

중요

단조적 버전 증가

마이그레이션은 realm을 더 높은 스키마 버전으로 업데이트해야 합니다. 클라이언트 애플리케이션이 영역의 현재 버전보다 낮은 스키마 버전의 realm을 열거나 지정된 스키마 버전이 realm의 현재 버전과 동일하지만 다른 객체 스키마를 포함하는 경우 realm에서 오류가 발생합니다.

로컬 마이그레이션 은 다른 Realm과 자동으로 동기화 되지 않는 Realm에 대한 마이그레이션입니다. 로컬 마이그레이션은 기존 Realm 스키마, 버전 및 객체에 액세스할 수 있으며 Realm을 새 스키마 버전으로 점진적으로 업데이트하는 로직을 정의합니다. 로컬 마이그레이션을 수행하려면 현재 버전보다 높은 새 스키마 버전을 지정하고 오래된 Realm을 열 때 마이그레이션 기능을 제공해야 합니다.

iOS에서는 수동 마이그레이션을 사용하여 스키마 변경 사항을 반영하도록 기초 데이터를 업데이트할 수 있습니다. 이러한 수동 마이그레이션 중에 스키마에 추가 또는 제거될 때 새 속성과 삭제된 속성을 정의할 수 있습니다.

Realm은 추가된 속성을 자동으로 마이그레이션할 수 있지만 이러한 변경을 수행할 때는 업데이트된 스키마 버전을 지정해야 합니다.

참고

Realm은 새로운 필수 속성에 대한 값을 자동으로 설정하지 않습니다. 마이그레이션 블록을 사용하여 새 필수 속성에 대한 기본값을 설정해야 합니다. 새로운 선택적 속성의 경우, 기존 레코드는 null 값을 가질 수 있습니다. 이는 선택적 속성을 추가할 때 마이그레이션 블록이 필요하지 않음을 의미합니다.

예시

스키마 버전 1을 사용하는 영역에는 이름, 성, 연령 속성이 있는 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
@Persisted 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 {
@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은 업데이트된 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을 설정합니다. 속성을 삭제해도 기존 객체에는 영향을 미치지 않습니다.

예시

스키마 버전 1을 사용하는 영역에는 이름, 성, 연령 속성이 있는 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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
@Persisted var age = 0
}

개발자는 Personage 필드가 필요하지 않다고 결정하고 스키마를 업데이트합니다.

// 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은 업데이트된 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()

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")
}
})

마이그레이션이 필요한 경우 deleteRealmIfMigrationNeeded 메서드를 사용하여 realm을 삭제할 수 있습니다. 개발 중에 빠르게 반복해야 하고 마이그레이션을 수행하고 싶지 않을 때 유용할 수 있습니다.

사용자 지정 마이그레이션 로직을 정의하려면 Realm을 열 때 구성 의 migrationBlock 속성을 설정합니다 .

마이그레이션 블록은 마이그레이션을 수행하는 데 사용할 수 있는 마이그레이션 객체를 받습니다. 마이그레이션 객체의 enumerateObjects(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 {
@Persisted var firstName = ""
@Persisted var lastName = ""
@Persisted var age = 0
}

개발자는 Person 클래스가 별도의 firstNamelastName 필드 대신 fullName 필드를 사용하도록 결정한 후 스키마를 업데이트합니다.

Realm이 업데이트된 Person 스키마를 따르도록 마이그레이션하려면 개발자는 영역의 스키마 버전을 2로 설정하고 기존 firstNamelastName 속성을 기준으로 fullName 값을 설정하는 마이그레이션 함수를 정의합니다.

// 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()

나중에 개발자는 age 필드를 Int 대신 String 유형으로 지정하기로 결정하고 스키마를 업데이트합니다.

업데이트된 Person 스키마를 준수하도록 영역을 마이그레이션하기 위해 개발자는 영역의 스키마 버전을 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 {
@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()

선형 마이그레이션

마이그레이션 블록에서 if (oldSchemaVersion < X)문을 중첩하거나 생략하지 않도록 합니다. 이렇게 하면 클라이언트가 어떤 스키마 버전에서 시작하든 상관없이 모든 업데이트를 올바른 순서로 적용할 수 있습니다. 목표는 오래된 스키마 버전의 데이터를 현재 스키마와 일치하도록 변환할 수 있는 마이그레이션 로직을 정의하는 것입니다.

임베디드 객체는 부모 객체와 독립적으로 존재할 수 없습니다. 객체를 EmbeddedObject로 변경할 때, 마이그레이션 블록은 모든 임베디드 객체가 정확히 하나의 부모 객체로의 역링크를 가지도록 해야 합니다. 역링크가 없거나 혹은 여러 개 있는 경우 다음과 같은 예외가 발생합니다.

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

다음도 참조하세요.

Realm-swift리포지토리 에서 추가 마이그레이션 예시를 확인하세요.

돌아가기

지원되는 유형