ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Ch6. Versioning & Migration
    Ray Wenderlich/CoreData 2022. 10. 27. 22:41

    마이그레이션이 필요한 아래 시나리오를 구현해본다.

    [노트를 작성할 수 있는 앱]

    1. 텍스트를 작성할 수 있다.
    2. 이미지를 추가할 수 있도록 변경. (Lightweight migration)
    3. 이미지를 여러장 추가할 수 있도록 변경. (Manual migration using Mapping model)
    4. 이미지가 아닌 다른 리소스도 첨부할 수 있도록 변경. (Manual migration using Mapping model and subclass of NSEntityMigrationPolicy)
    5. 연속적이지 않은 버전의 마이그레이션을 지원하기 Ex) v2 → v4

    The migration process

    마이그레이션은 3단계로 나뉜다.

    1. First, Core Data copies over all the objects from one data store to the next.
    2. Next, Core Data connects and relates all the objects according to the relationship mapping.
    3. Finally, enforce any data validations in the destination model. Core Data disables destination model validations during the data copy.

    +마이그레이션이 에러없이 끝나면 오리지널 데이터를 삭제한다.

    Lightweight migrations

    Apple에서 사용한 용어다. NSPersistentContainer를 사용한다면 자동으로 마이그레이션이 일어난다. 커스텀 코어 데이터 스택을 사용한다면 몇개의 플래그를 설정하여 자동으로 마이그레이션을 수행할 수 있다. 데이터 변경에 대한 제한사항이 좀 있다.

    Ex) 노트를 구현하기 위해 title, body, date로만 이루어진 엔티티. 하지만 사진을 삽입하는 기능을 추가한다고 할 때, photo라는 attribute가 추가된다.

    1. XCode - Editor - Add Model Version - 새로운 버전 생성
    2. File inspector의 Model Version에서 새로 생성한 이름의 버전을 선택

    Manual migrations

    Old 데이터셋을 New 데이터셋으로 맵핑할 방법을 지정해주어야 한다. XCode에서 이 작업을 한다면 새로운 데이터모델을 만드는 것과 비슷하다.

    Custom manual migrations

    Manual과 동일하게 데이터셋 맵핑을 해주어야 한다. 다만 데이터를 이동하면서 커스텀 변환 로직이 추가된다. 이 로직은 NSEntityMigrationPolicy의 서브클래스를 생성하는 것을 포함한다.

    Fully manual migrations

    커스텀 로직만으로 데이터셋을 옮기는 것이 충분치 않을 때 사용한다. 버전 검사와 마이그레이션을 핸들링하는 과정을 모두 수동으로 해주어야 한다.

    Inferred mapping models

    NSPersistentStoreDescription의 shouldInferMappingModelAutomatically를 true로 설정하면 자동으로 모델 매핑을 추론한다. Old버전과 New버전의 Entity와 Attribute가 동일하다면 그대로 복사가 이루어 진다. 그렇지 않은 경우, 몇 가지 룰을 따르게 된다.

    lightweight 마이그레이션을 할 때, 명확한 패턴을 갖고 있어야 한다.

    • Entity, Attribute, Relationship를 삭제
    • renamingIdentifier(?)을 사용하여 Entity, Attribute, Relationship의 이름을 변경
    • 새로운 Optional Attribute 추가
    • 디폴트 값을 가지는 필수(required) Attribute 추가
    • Optional Attribute를 디폴트 값을 갖는 Non-Optional Attribute로 변경
    • Non-Optional Attribute를 Optional Attribute으로 변경
    • Entity의 구조(hierachy) 변경
    • 새로운 부모 Entity를 추가하고 Attribute를 계층에 따라 이동시킴
    • to-one관계에서 to-many관계로 변경
    • non-ordered, to-many관계에서 ordered, to-many 관계로 변경 (또는 반대로)

    For more information: Using Lightweight Migration

    A manual migration

    Mapping models

    변경사항이 복잡해 Lightweight 마이그레이션을 사용할 수 없다면 Manual 마이그레이션을 사용한다. Manual 마이그레이션을 사용하기 위해서 Mapping model을 생성해야 한다.

    Attribute mapping

    Mapping model의 Entity Mappings를 보면 엔티티간의 Attribute 매핑에 대한 정보가 있다.

    Relationship mapping

    Entity에 있는 Attribute로 새로운 Entity를 만들었다면 아래 스샷과 같이 관계를 매핑해줄 수 있다.

    마지막으로 마이그레이션을 자동으로 추론하는 것을 방지하기 위해 코드상으로 작업을 해주어야 한다.

    description.shouldMigrateStoreAutomatically = true
    description.shouldInferMappingModelAutomatically = false

    A complex mapping model

    Mapping model에 커스텀 로직을 추가해야 한다면 NSEntityMigrationPolicy를 상속받아 코드를 작성할 수 있다. createDestinationInstances메소드를 오버라이드하여 로직을 작성하고 Mapping model에는 NSEntityMigrationPolicy의 서브클래스로 선언한 클래스의 경로를 작성해주면 된다.

    Migrating non-sequential versions

    v1에서 v4로 마이그레이션 하는 상황이 생길 수 있다. 커스텀 마이그레이션 매뉴얼을 코드로 작성하여 구현할 수 있다. 앱스토어에 릴리즈하기 전에 충분한 테스트가 필요하다.

    func performMigration() {
        if !currentModel.isVersion4 {
          fatalError("Can only handle migrations to version 4!")
        }
    
        if let storeModel = self.storeModel {
          if storeModel.isVersion1 {
            let destinationModel = NSManagedObjectModel.version2
            migrateStoreAt(
              URL: storeURL,
              fromModel: storeModel,
              toModel: destinationModel
            )
            performMigration()
          } else if storeModel.isVersion2 {
            let destinationModel = NSManagedObjectModel.version3
            let mappingModel = NSMappingModel(
              from: nil,
              forSourceModel: storeModel,
              destinationModel: destinationModel
            )
            migrateStoreAt(
              URL: storeURL,
              fromModel: storeModel,
              toModel: destinationModel,
              mappingModel: mappingModel
            )
            performMigration()
          } else if storeModel.isVersion3 {
            let destinationModel = NSManagedObjectModel.version4
            let mappingModel = NSMappingModel(
              from: nil,
              forSourceModel: storeModel,
              destinationModel: destinationModel
            )
            migrateStoreAt(
              URL: storeURL,
              fromModel: storeModel,
              toModel: destinationModel,
              mappingModel: mappingModel
            ) }
        }
      }

    'Ray Wenderlich > CoreData' 카테고리의 다른 글

    Ch8. Measuring & Boosting Performance  (0) 2022.10.31
    Ch7. Unit Testing  (0) 2022.10.31
    Ch5. NSFetchedResultsController  (0) 2022.10.27
    Ch2. NSManagedObject Subclasses  (0) 2022.10.27
Designed by Tistory.