Основные данные.Как поменять NSPersistentStores и сообщить NSFetchedResultsController? - PullRequest
0 голосов
/ 01 июня 2018

Я выполняю резервное копирование и восстановление (через Dropbox) пользовательских Core Data постоянных данных.Для восстановления я извлекаю файлы из Dropbox и временно сохраняю их в каталоге Documents.Затем я создаю новый NSPersistentContainer и использую его для замены текущего постоянного хранилища (в каталоге ApplicationSupport) перед удалением ненужных файлов в каталоге Documents.

Сейчас я использую шаблон MasterDetailи поэтому у меня есть обычные сущности с метками времени и NSFetchedResultsController, которые идут вместе с этим.После замены я возвращаюсь к основному tableView и, к сожалению, вижу исходный набор сущностей.Когда приложение перезагружается, я вижу восстановленные данные, как и предполагалось, но необходимо выяснить, как заставить NSFetchedResultsController автоматически видеть новые восстановленные данные.

Это мой код восстановления:

func restorePSFromBackup() {
    // Current persistent store is in the ApplicationSupport directory.
    let currentPSFolderUrl = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!
    // The current Core Data file (url).
    let currentPSUrl1 = currentPSFolderUrl.appendingPathComponent("DBCDTest.sqlite")
    // Backup persistent store with which to restore is in the Documents directory.
    let backUpFolderUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    // The 3 backup Core Data files (urls).
    let backupUrl1 = backUpFolderUrl.appendingPathComponent("DBCDTest.sqlite")
    let backupUrl2 = backUpFolderUrl.appendingPathComponent("DBCDTest.sqlite-wal")
    let backupUrl3 = backUpFolderUrl.appendingPathComponent("DBCDTest.sqlite-shm")
    let sourceSqliteURLs = [backupUrl1, backupUrl2, backupUrl3]

    let container = NSPersistentContainer(name: "DBCDTest")

    do {
        // Replace current persistent store with the restore/backup persistent store.
        try container.persistentStoreCoordinator.replacePersistentStore(at: currentPSUrl1, destinationOptions: nil, withPersistentStoreFrom: backupUrl1, sourceOptions: nil, ofType: NSSQLiteStoreType)
        // Delete the restore/backup files from the Application directory.
        do {
            for index in 0..<sourceSqliteURLs.count {
                try FileManager.default.removeItem(at: sourceSqliteURLs[index])
            }
        } catch let error {
            print("Failed to delete sqlite files.")
            print(error.localizedDescription)
        }
    } catch let error {
        print("Failed to replace persistent store.")
        print(error.localizedDescription)
    }
}

Вместо создания нового NSPersistentContainer я попытался использовать инъекцию зависимостей для ссылки на созданную в AppDelegate и использовать ее для выполнения подкачки, но при попытке заменить постоянные хранилища не удается.Может ли это быть какая-то проблема NSManagedObjectContext?Может ли потребоваться очистка перед доступом к новому хранилищу данных?

1 Ответ

0 голосов
/ 04 июня 2018

Мне удалось собрать ответ из нескольких ответов на похожие вопросы, в частности, этот ответ из Том Харрингтон .Короче говоря, мне нужно было:

  1. Создать новое NSPersistentContainer.
  2. Заменить текущее постоянное хранилище на постоянное хранилище восстановления / резервного копирования, используя метод replacePersistentStore наpersistentStoreCoordinator.
  3. Уничтожить хранилище резервных копий.
  4. Удалить файлы, связанные с хранилищем резервных копий.
  5. Восстановить стек Core Data в AppDelegate.
  6. Сохранение, ноль, а затем повторная инициализация managedObjectContext и NSFetchedResultsController.

Number 6 MasterViewController позволили мне увидеть свет.Мой последний метод восстановления:

func restorePSFromBackup() {
    // Current persistent store is in the ApplicationSupport directory.
    let currentPSFolderUrl = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!
    // The current Core Data file (url).
    let currentPSUrl1 = currentPSFolderUrl.appendingPathComponent("DBCDTest.sqlite")
    // Backup persistent store with which to restore is in the Documents directory.
    let backUpFolderUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    // The 3 backup Core Data files (urls).
    let backupUrl1 = backUpFolderUrl.appendingPathComponent("DBCDTest.sqlite")
    let backupUrl2 = backUpFolderUrl.appendingPathComponent("DBCDTest.sqlite-wal")
    let backupUrl3 = backUpFolderUrl.appendingPathComponent("DBCDTest.sqlite-shm")
    let sourceSqliteURLs = [backupUrl1, backupUrl2, backupUrl3]

    let container = NSPersistentContainer(name: "DBCDTest")

    do {
        // Replace current persistent store with the restore/backup persistent store.
        try container.persistentStoreCoordinator.replacePersistentStore(at: currentPSUrl1, destinationOptions: nil, withPersistentStoreFrom: backupUrl1, sourceOptions: nil, ofType: NSSQLiteStoreType)
        // Destroy the backup store.
        try container.persistentStoreCoordinator.destroyPersistentStore(at: backupUrl1, ofType: NSSQLiteStoreType, options: nil)
        // Delete the restore/backup files from the Application directory.
        do {
            for index in 0..<sourceSqliteURLs.count {
                try FileManager.default.removeItem(at: sourceSqliteURLs[index])
            }
        } catch let error {
            print("Failed to delete sqlite files.")
            print(error.localizedDescription)
        }
        // Rebuild the AppDelegate's Core Data stack.
        (UIApplication.shared.delegate as! AppDelegate).persistentContainer = NSPersistentContainer(name: "DBCDTest")
        (UIApplication.shared.delegate as! AppDelegate).persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
            print(NSPersistentContainer.defaultDirectoryURL())
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            } else {
                // Save, nil and then reinitialise the MasterViewController managedObjectContext and NSFetchedResultsController.
                do {
                    try self.masterViewController.managedObjectContext?.save()
                } catch let error {
                    print("Failed to save managedObjectContext.")
                    print(error.localizedDescription)
                }
                self.masterViewController.managedObjectContext = nil
                self.masterViewController.managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
                self.masterViewController._fetchedResultsController = nil
                let _ = self.masterViewController.fetchedResultsController
            }
        })
    } catch let error {
        print("Failed to replace persistent store.")
        print(error.localizedDescription)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...