LoginSignup
63
62

More than 5 years have passed since last update.

CoreDataのマイグレーションをしようとしたときのメモ(未解決あり)

Last updated at Posted at 2014-02-05

今回は、CoreDataの手動マイグレーションをやってみようとしてハマったのでそのメモです。
最終的には自動マイグレーションで終わらせました。ある程度の手動マイグレーションの感覚は掴めた気がしていますが、なぜ最後まで動かなかったのかは未解決です・・。

マイグレーション

まず、マイグレーションの手順。

  1. CoreDateのモデルファイル(XXXX.xcdatamodeld)を選択した状態でメニューから「Editor > Add Model Version…」を選択し、新しいバージョンのモデルファイルを生成

  2. 新規作成した新しいバージョンのモデルデータを選択した状態で プロパティなど、Entityを編集
    スクリーンショット 2014-02-05 20.48.27.png
    「QiitaModel 2.xcdatamodel」が新しく生成したモデルファイル

  3. 修正後、新規ファイル作成で「Core Date > Mapping Model」ファイルを生成
    スクリーンショット 2014-02-05 20.49.58.png

  4. 生成時、移行元と移行先のモデルデータを選択する必要があるので、移行元→移行先の順でファイルを指定してマッピングファイルを作る

  5. 生成したマッピングファイルを選択し、Inspectorの「Custom Policy」の欄に、マイグレーションを処理するクラスを指定する
    cap.png

  6. (5)で指定したクラスを新規作成する。継承元クラスはNSEntityMigrationPolicyクラス

  7. マイグレーションが発生した際、アプリケーションは自動でマイグレーション用のデリゲートメソッドを呼び出す

  8. 1レコードあたり1回ずつデリゲートメソッドが呼ばれ、その中で移行元のコンテキストから値を取り出し、移行先のデータに手動でコピーしていく。(新規のデータについてはデフォルト値があればそこも手動で)

  9. すべての処理が終わったら、無事マイグレーション完了。

ただ、この「自動でマイグレーション用のデリゲートメソッドが呼ばれる、というところでなぜか呼ばれず、どうしても解決しなかったので自動マイグレーションで対応しました

サンプルコード

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error
{
    NSManagedObjectContext *context = [manager destinationContext];
    NSString *entityName [mapping destinationEntityName];
    NSManagedObject *dInstance = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];

    [dInstance setValue:@"value1" forKey:@"anyKey1"];
    [dInstance setValue:@"value2" forKey:@"anyKey2"];
    //…

    [dInstance setValue:[sInstance valueForKey:@"existsValue1" forKey:@"existsKey1"];
    [dInstance setValue:[sInstance valueForKey:@"existsValue2" forKey:@"existsKey2"];
    //…

    return YES;
}

ざっくりやっていることを言うと、前のデータにあるやつはそれを取り出し新しいデータに移行し、ないのはデフォルト値なりを設定する、というもの。手動コピーみたいな感じ。
シンプルな場合はそれだけだけど、Relationとかが絡んでくると色々と大変になるはず。

見てもらうと分かるけど、ひとつのNSManagedObjectのみ処理している。つまり、管理しているデータ件数分、このメソッドが呼ばれるということ。なので、あまりに重い場合は処理を分けるなどして起動時のタイムアウトに引っかからないようにする必要があるみたい。

自動マイグレーションで対応

大掛かりな変更でなければ、おそらくさくっとできる自動マイグレーションで十分。
やり方は、NSPersistentStoreCoordinatorをインスタンス化する際にオプションを指定するだけ。

//自動マイグレーション用にオプションを指定
NSDictionary *options = @{NSInferMappingAutomaticallyOption:@YES,
               NSMigratePersistentStoresAutomaticallyOption:@YES};

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
    //Error!
}

現在のModel Versionを指定する

新しくモデルを定義しても、定義の参照先を変更しないと使えません。
バージョンの変更は、CoreDataのモデルデータを選択し、InspectorからModel Versionを変更したいものに変更する必要があります。

cap.png

マイグレーション必要かどうか判断する

マイグレーションが必要かどうかはNSPersistentStoreCoordinatorNSManagedObjectContextを使うことでチェックできる。

//meta dataを取得する
NSDictionary *sourceMetaData = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeURL error:&error];

//コンテキストに適合性を問い合わせる
BOOL isCompatible = [_managedObjectContext isConfiguration:nil compatibleWithStoreMetadata:sourceMetaData];

if (!isCompatible) {
    //マイグレーションが必要
}

ハマったこと

マイグレーションをやってみようと思ってごにょごにょしてるときに、とりあえず元に戻そうと差分をgitから戻したあと、Compilation failed for data model at path '/path/to/new/model.mom'みたいなエラーでビルドできなくなった。

ファイルとかは元に戻したのになぜ・・と思っていて、↓のところの矢印部分をクリックしたら、そこに新規で作ったファイルがまだ残っていた。それのせいでエラーになっていた模様。それを消したら無事ビルドできた。

cap.png


参考にした記事

63
62
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
62