CoreData UIManagedDocumentでマイグレーション

CoreDataのUIManagedDocumentを使用している場合のマイグレーション方法について紹介します (iPhone SDKのバージョンはXcode 4.4.xとします)。

以下のような方が対象です。
- UIManagedDocumentをCoreDataとして使用している
- アプリのアップデートに伴い、DBのテーブル構造を変更した
- テーブル構造を変更したら、アプリがクラッシュする(NSInternalInconsistencyException)
- 古いバージョンのアプリをいったん削除してインストールし直すと問題が発生しない

話を具体化するために以下のケースを想定して話を進めます
- UIManagedDocumentを使用してアプリが動作している
- アプリ名はMyAppで、バージョン1.0.0を開発済みで1.0.1を現在開発中
- 1.0.1ではテーブル構造を変更する(Bookmarkにtitleを追加する)
- 1.0.0時点でのxcdatamodel名はMyDM、Entity名はBookmark、Attribute名はurl (String型)で、title(String型)を新規に追加する

以下のステップでUIManagedDocumentのマイグレーションを行います。
1. Model Versionを追加、Currentを変更
2. Mapping Modelを作成、Custom Policyを入力
3. Custom Policyに対応するEntity Migration Policyクラスを新規作成
4. -(BOOL)createDestinationInstancesForSourceInstance: entityMapping: manager: error:に追加するAttributeを記述する
5. UIManagedDocumentオブジェクトにオプションを指定
6. 動作確認テスト


1. Model Versionを追加、Currentを変更
Xcodeのメニューから[Editor] - [Add Model Version]を選択します。
[Based on model]から"MyDM"を選択、[Version name]にMyDM_1.0.1と入力します。
MyDM.xcdatamodeldの中に、MyDM.xcdatamodelとMyDM_1.0.1.xcdatamodelがあります。
MyDM_1.0.1.xcdatamodelを選択状態に、Bookmarkをクリックして、String型のtitleを追加します(MyDM.xcdatamodelは変更しません)。

メニューから[View] - [Utilities] - [Show Utilities]として右側に情報パネルを表示します。
左側ツリーからMyDM.xcdatamodeldを選択し、右側パネルの[Versioned Core Data Model] - [Current]から"MyDM_1.0.1"を選択します(左側ツリーでMyDM_1.0.1.xcdatamodelにチェックマークが付くはずです)。


2. Mapping Modelを作成、Custom Policyを入力
メニューから[File] - [New] - [CoreData] - [Mapping Model]を選択します。
[Mapping Model Source Data Model]ではMyDM.xcdatamodelを指定します。
[Mapping Model Target Data Model]ではMyDM_1.0.1.xcdatamodel]を指定します。
保存名は、"Model_1.0.0to1.0.1.xcmappingmodel"とします。
左側ツリーからModel_1.0.0to1.0.1.xcmappingmodelを選択、[ENTITY MAPPING]から[Bookmark]を選択します。
[View] - [Utilities] - [Show Mapping Model Inspector]を選ぶと右側パネルが切り替わります。
[Custom Policy]に"BookmarkEntityMigrationPolicy"と入力します。


3. Custom Policyに対応するEntity Migration Policyクラスを新規作成
[File] - [New] - [Cocoa Touch] - [Objective-C class]とメニューをたどり、[Class]に"BookmarkEntityMigrationPolicy"、[Subclass of]に"NSEntityMigrationPolicy"と入力します。


4. -(BOOL)createDestinationInstancesForSourceInstance: entityMapping: manager: error:に追加するAttributeを記述する
BookmarkEntityMigrationPolicy.mを開き編集します。

@implementation BookmarkEntityMigrationPolicy
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance 
    entityMapping:(NSEntityMapping *)mapping
    manager:(NSMigrationManager *)manager
    error:(NSError *__autoreleasing *)error
{
    NSManagedObjectContext *context = [manager destinationContext];
    NSString *entityName = [mapping destinationEntityName];
    
    NSString *url = [sInstance valueForKey:@"url"];
    
    NSManagedObject *dInstance = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
    // Old column
    [dInstance setValue:url forKey:@"url"];
    // New column
    NSString *title = @"Unknown";
    [dInstance setValue:title forKey:@"title"];
    
    return YES;
}
@end



5. UIManagedDocumentオブジェクトにオプションを指定
UIManagedDocumentの変数名を_databaseとすると、_database生成後にオプションを指定します。

  _database = [[UIManagedDocument alloc] initWithFileURL:url];


の直後に、

  NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
  _database.persistentStoreOptions = options;

を追加します。
UIManagedDocumentの生成場所/タイミングはそれぞれ違うので、各々のケースに合わせてください。


6. 動作確認テスト
- MyApp ver. 1.0.0を実機にて転送/実行します。
- ver. 1.0.1のプロジェクトで作成した、BookmarkEntityMigrationPolicy.mのcreateDestinationInstancesForSourceInstance...メソッド内にブレークポイントを設定します。
- MyApp ver. 1.0.1の実機にてデバッグ実行します。
- アプリを起動後、createDestinationInstancesForSourceInstanceのブレークポイントが反応すれば上の設定はだいたいうまく行っているはずです。
- アプリがクラッシュしなければ、マイグレーションは成功です。

Comments

Popular posts from this blog

Swing Manager für iPhone / iPad [Deutsch]