CoreDataのsqliteデータをファイル共有で見えないように移行する
アプリver1でCoreDataのリストアプリを作成済みで、今回のver2でファイル共有機能を追加したいと思っています。けれど、ファイル共有でsqliteファイルがユーザーから見えてしまうので、sqliteの置き場所を変更しようと思います。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
略)
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyApp.sqlite"];
略)
}
上の処理を書き換えて下のようにしました。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject];
NSError *error = nil;
if ( ![[NSFileManager defaultManager] fileExistsAtPath:applicationSupportDirectory isDirectory:NULL] ) {
if (![[NSFileManager defaultManager]
createDirectoryAtPath:applicationSupportDirectory
withIntermediateDirectories:NO
attributes:nil
error:&error]) {
NSAssert(NO, ([NSString stringWithFormat:@"Failed to create App Support directory %@ : %@", applicationSupportDirectory,error]));
NSLog(@"Error creating application support directory at %@ : %@",applicationSupportDirectory,error);
return nil;
}
}
NSURL *storeURL = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"MyApp.sqlite"]];
}
このようにしてみましたが、新規ダウンロードの場合は上手くいくと思うのですがアップデートの際に問題が残ります。
既存のsqliteの移行処理はどのようにしたらよいのでしょうか?
追記 8/17 23:24
いただいたコメントを元に実装してみました。書く位置によってハマるところがあったり、iOS7移行は sqlite.shm やsqlite.wal などのファイルもあってかなり苦労したのでうまくいった例として
掲載します。
sqlite,sqlite.shm,sqlite.wal ファイル共有から丸見え状態→プログラム書き換え→ビルド→ファイル隠し成功&sqliteデータの引き継ぎ成功→ビルド→問題なし。まで確認しました。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
//meta dataを取得する
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = @{NSInferMappingModelAutomaticallyOption:@YES,
NSMigratePersistentStoresAutomaticallyOption:@YES};
//sqlite移行処理
NSURL *oldStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyApp.sqlite"];
NSURL *oldStoreURL_shm = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyApp.sqlite-shm"];
NSURL *oldStoreURL_wal = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyApp.sqlite-wal"];
NSError *error = nil;
NSString *failureReason = @"There was an error creating or loading the application's saved data.";
NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject];
//移行用ディレクトリがなければ新たに作成
if ( ![[NSFileManager defaultManager] fileExistsAtPath:applicationSupportDirectory isDirectory:NULL] ) {
if (![[NSFileManager defaultManager]
createDirectoryAtPath:applicationSupportDirectory
withIntermediateDirectories:NO
attributes:nil
error:&error]) {
NSAssert(NO, ([NSString stringWithFormat:@"Failed to create App Support directory %@ : %@", applicationSupportDirectory,error]));
NSLog(@"Error creating application support directory at %@ : %@",applicationSupportDirectory,error);
return nil;
}
}
NSURL *storeURL = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"MyApp.sqlite"]];
NSURL *storeURL_shm = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"MyApp.sqlite-shm"]];
NSURL *storeURL_wal = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"MyApp.sqlite-wal"]];
NSFileManager *fileManager = [[NSFileManager alloc]init];
//今までの保存先にファイルがあれば移行処理に入る
if ([fileManager fileExistsAtPath:oldStoreURL.path]) {
NSPersistentStore *oldStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:oldStoreURL
options:options
error:nil];
// 新しい場所にコピー
[_persistentStoreCoordinator migratePersistentStore:oldStore
toURL:storeURL
options:options
withType:NSSQLiteStoreType
error:nil];
//shmとwalを移行用のURLにコピー
[fileManager copyItemAtURL:oldStoreURL_shm toURL:storeURL_shm error:nil];
[fileManager copyItemAtURL:oldStoreURL_wal toURL:storeURL_wal error:nil];
//移行前のsqlite本体を削除
[fileManager removeItemAtURL:oldStoreURL error:nil];
//移行前のshmとwalを削除
[fileManager removeItemAtURL:oldStoreURL_shm error:nil];
[fileManager removeItemAtURL:oldStoreURL_wal error:nil];
//この下の文を if(~ )のままにすると、評価式に入って2度
//-(addPersistentStoreWithType:略)が呼び出されることになりアプリが落ちるので、else if にしておきます。
//一度正常に動けば上のif文は通過しないためバグは起きません。
}else if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}