Обновление основного хранилища данных для поддержки групп приложений


У меня уже есть приложение в App Store, которое использует основные данные для сохранения данных.
Теперь, когда выйдет iOS 8, я хочу добавить к нему виджет, поэтому я должен использовать группы приложений для обмена данными между двоичными файлами.
Одна проблема, хотя - мне нужно изменить местоположение магазина для поддержки групп приложений для всех существующих пользователей.
Я написал следующий код, пытаясь переместить магазин на новый путь:

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    NSURL *oldStoreURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    oldStoreURL = [oldStoreURL URLByAppendingPathComponent:@"Schooler.sqlite"];


    NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.schooler.mycontainer"];
    storeURL = [storeURL URLByAppendingPathComponent:@"Schooler.sqlite"];


    if([[NSFileManager defaultManager] fileExistsAtPath:oldStoreURL.path] == YES && [[NSFileManager defaultManager] fileExistsAtPath:storeURL.path] == NO)
    {
        // Prior today extension - Need to move to new directory
        NSError *error = nil;
        if([[NSFileManager defaultManager] moveItemAtURL:oldStoreURL toURL:storeURL error:&error] == YES)
            NSLog(@"Migrated successfully to new database location.");
        else
            NSLog(@"error: %@",error);
    }

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

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }

    return _persistentStoreCoordinator;
}

Вывод всегда "успешно перенесен в новое местоположение базы данных", хотя все данные, которые были сохранены в приложении ранее, были удалены, как если бы оно создало новую базу данных вместо того, чтобы просто переместить ее.

В чем причина проблемы? Как мне это исправить?
Спасибо.

2 ответа

Решение

Базовое хранилище Core Data NSSQLiteStoreType, созданное с параметрами по умолчанию, на самом деле представляет собой несколько файлов, как описано в Технических вопросах и ответах 1809. Новый режим ведения журнала по умолчанию для хранилищ Core Data SQLite в iOS 7 и OS X Mavericks. Это важно помнить при попытке переместить магазин за пределы процесса миграции, и это является источником вашей проблемы - вы перемещаете один файл, когда вам нужно переместить все из них. Однако не рекомендуется перемещать файлы отдельно от Core Data без использования координатора файлов. Вместо этого лучше использовать миграцию.

Миграция возьмет данные из исходного хранилища и перенесет их в новое хранилище, по сути, реплицируя старые данные в новом месте. Старые данные все еще будут существовать в файловой системе. В вашем приложении вы должны выполнить миграцию в том виде, в каком вы сейчас находитесь, но не пытайтесь самостоятельно перемещать старые данные в новое местоположение - именно здесь все идет не так.

Вместо того, чтобы перемещать файлы вокруг себя, вы можете положиться на миграцию для перемещения данных за вас. Сначала добавьте хранилище в координатор постоянного хранилища с URL-адресом исходных данных. Затем вы выполните миграцию, чтобы переместить эти данные на новый URL.

NSPersistentStore   *sourceStore        = nil;
NSPersistentStore   *destinationStore   = nil;
NSDictionary        *storeOptions       = @{ NSSQLitePragmasOption : @{ @"journal_mode" :
  @"WAL" } };

// Add the source store
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldStoreURL options:storeOptions error:&error]){
    // Handle the error
} else {
    sourceStore = [coordinator persistentStoreForURL:oldStoreURL];
    if (sourceStore != nil){
        // Perform the migration
        destinationStore = [coordinator migratePersistentStore:sourceStore toURL:storeURL options:storeOptions withType:NSSQLiteStoreType error:&error];
        if (destinationStore == nil){
            // Handle the migration error
        } else {
            // You can now remove the old data at oldStoreURL
            // Note that you should do this using the NSFileCoordinator/NSFilePresenter APIs, and you should remove the other files
            // described in QA1809 as well.
        }
    }
}

После завершения миграции вы можете удалить старые файлы. В приведенном здесь примере явно указываются параметры журнала SQLite, чтобы гарантировать, что в случае изменения параметров по умолчанию в будущем код все равно будет работать. Если вы используете разные варианты, вы должны использовать их вместо.

В случае, если версия на Swift будет полезна:

    let oldPersistentStoreURL: URL = ...
    let sharedPersistentStoreURL: URL = ...
    let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]    // + any database-specific options

    if FileManager.default.fileExists(atPath: oldPersistentStoreURL.path) {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
        do {
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: oldPersistentStoreURL, options: options)
            if let sourceStore = coordinator.persistentStore(for: oldPersistentStoreURL) {
                let _ = try coordinator.migratePersistentStore(sourceStore, to: sharedPersistentStoreURL, options: options, withType: NSSQLiteStoreType)
                // If migration was successful then delete the old files
            }
        } catch {
            error.logErrors()
        }
    }
Другие вопросы по тегам