[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🍎 Apple/SwiftData & CoreData

[SwiftData] Migration과 버전관리

inu 2023. 8. 3. 18:33

본 게시글은 WWDC 2023의 Model your schema with SwiftData 세션의 내용을 정리한 것입니다.

https://developer.apple.com/videos/play/wwdc2023/10195


Modeling 복습

  • SwiftData를 Import하고 class에 @Model 매크로를 부여하는 것으로 스키마를 생성할 수 있다.
  • @Attribute(.unique) 매크로를 통해 특정 속성을 유니크하게 만들 수 있다. 만약 이미 존재하는 프로퍼티를 Insert하려 하면 기존에 존재하던 데이터를 업데이트한다. (upsert)
  • 기존에 존재하던 속성의 이름을 그냥 바꾸면 단순히 새로운 속성만 생성되고 이전 속성의 데이터는 모두 삭제된다. 이름을 바꾸고 기존의 데이터를 보유하기 위해서는 @Attribute(originalName: "oldName") 매크로를 속성에 적용해주어야한다.
  • 이 외에도 @Attribute 는 대용량 데이터를 외부에 저장하거나 Tansformable을 지원하는 등 다양한 기능을 지원한다.
  • SwiftData는 같은 Model Class의 프로퍼티를 Relationship으로 인식한다. 이러한 Relationship에 아무런 옵션을 주지 않으면 해당 관계를 가진 모델의 데이터가 삭제되었을 때 해당 부분은 nil값으로 전환된다.
  • @Relationship(.cascade) 매크로를 적용하면 관계를 가진 모델의 데이터가 삭제되면 해당 데이터도 삭제된다.
  • 이 외에도 @Relationship 은 originalName를 수정하거나 최소 및 최대 개수 제약조건을 부여하는 등의 기능을 지원한다.
  • 스키마에서 특정 속성을 제외하고 싶다면 @Transient 매크로를 부여하면 된다. (기본값 필수)

Migration

프로퍼티를 제거하거나 추가하는 등 스키마에 변경을 가하면 Data Migration이 발생한다. 마이그레이션은 꽤나 복잡한 작업이지만, VersionedSchema 와 SchemaMigrationPlan 이 이를 간편하게 수행할 수 있도록 도와준다.

 

VersionedSchema를 통해 이전에 사용하던 스키마를 캡슐화한다. 이렇게 버전을 관리함으로써 SwiftData가 스키마간의 변경사항을 인지할 수 있다. 그리고 이를 기반으로 SchemaMigrationPlan을 생성하여 마이그레이션을 어떻게 수행할지 정의한다.

 

마이그레이션에는 두가지 타입이 존재한다.

  • LightWeight Migration : 단순히 속성을 추가하거나 삭제 규칙을 지정하는 등 기존 데이터에 영향을 주지 않는 변경이 이에 해당한다. 기존 데이터를 마이그레이션하는 추가 코드가 필요하지 않다.
  • Custom Migration : 특정 속성을 Unique하게 바꾸는 등의 작업을 수행하는 경우 이에 해당한다.

Custom Migration 과정

아래는 Custom Migration의 과정이다.

 

먼저 스키마의 각 버전은 VesionedSchema enum안으로 넣는다.

enum SampleTripsSchemaV1: VersionedSchema { ​​​​static var models: [any PersistentModel.Type] { ​​​​​​​​[Trip.self, BucketListItem.self, LivingAccommodation.self] ​​​​} ​​​​@Model ​​​​final class Trip { ​​​​​​​​var name: String ​​​​​​​​var destination: String ​​​​​​​​var start_date: Date ​​​​​​​​var end_date: Date ​​​​​​​​var bucketList: [BucketListItem]? = [] ​​​​​​​​var livingAccommodation: LivingAccommodation? ​​​​} ​​​​// Define the other models in this version... } enum SampleTripsSchemaV2: VersionedSchema { ​​​​static var models: [any PersistentModel.Type] { ​​​​​​​​[Trip.self, BucketListItem.self, LivingAccommodation.self] ​​​​} ​​​​@Model ​​​​final class Trip { ​​​​​​​​@Attribute(.unique) var name: String ​​​​​​​​var destination: String ​​​​​​​​var start_date: Date ​​​​​​​​var end_date: Date ​​​​​​​​var bucketList: [BucketListItem]? = [] ​​​​​​​​var livingAccommodation: LivingAccommodation? ​​​​} ​​​​// Define the other models in this version... } enum SampleTripsSchemaV3: VersionedSchema { ​​​​static var models: [any PersistentModel.Type] { ​​​​​​​​[Trip.self, BucketListItem.self, LivingAccommodation.self] ​​​​} ​​​​@Model ​​​​final class Trip { ​​​​​​​​@Attribute(.unique) var name: String ​​​​​​​​var destination: String ​​​​​​​​@Attribute(originalName: "start_date") var startDate: Date ​​​​​​​​@Attribute(originalName: "end_date") var endDate: Date ​​​​​​​​var bucketList: [BucketListItem]? = [] ​​​​​​​​var livingAccommodation: LivingAccommodation? ​​​​} ​​​​// Define the other models in this version... }

그리고각 버전 변경에 대한 수정을 직접 Swift 코드로 작성한다.

enum SampleTripsMigrationPlan: SchemaMigrationPlan { ​​​​static var schemas: [any VersionedSchema.Type] { ​​​​​​​​[SampleTripsSchemaV1.self, SampleTripsSchemaV2.self, SampleTripsSchemaV3.self] ​​​​} ​​​​static var stages: [MigrationStage] { ​​​​​​​​[migrateV1toV2, migrateV2toV3] ​​​​} ​​​​static let migrateV1toV2 = MigrationStage.custom( ​​​​​​​​fromVersion: SampleTripsSchemaV1.self, ​​​​​​​​toVersion: SampleTripsSchemaV2.self, ​​​​​​​​willMigrate: { context in ​​​​​​​​​​​​let trips = try? context.fetch(FetchDescriptor<SampleTripsSchemaV1.Trip>()) ​​​​​​​​​​​​// De-duplicate Trip instances here... ​​​​​​​​​​​​try? context.save() ​​​​​​​​}, didMigrate: nil ​​​​) ​​​​static let migrateV2toV3 = MigrationStage.lightweight( ​​​​​​​​fromVersion: SampleTripsSchemaV2.self, ​​​​​​​​toVersion: SampleTripsSchemaV3.self ​​​​) }

Migration이 필요한 데이터는 Container에서 불러올 때 MingrationPlan을 추가해주어야 한다.

'🍎 Apple > SwiftData & CoreData' 카테고리의 다른 글

[WWDC24] What's new in SwiftData  (7) 2024.07.10
[SwiftData] ModelContainer, ModelContext  (0) 2023.08.03
[SwiftData] Meet SwiftData  (0) 2023.07.29
[SwiftData] 기본설명 및 특징  (0) 2023.07.29
[Core Data] Core Data Stack  (1) 2022.06.09