核心数据。如何交换NSPersistentStores并通知NSFetchedResultsController? [英] Core Data. How to swap NSPersistentStores and inform NSFetchedResultsController?

查看:107
本文介绍了核心数据。如何交换NSPersistentStores并通知NSFetchedResultsController?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实施(通过Dropbox)用户核心数据持久数据的备份和还原。为了进行还原,我从Dropbox中提取了文件并将它们临时存储在Documents目录中。然后,我创建一个新的 NSPersistentContainer 并使用它替换当前的持久性存储(在ApplicationSupport目录中),然后再删除Documents目录中不再需要的文件。

I'm implementing backup and restore (via Dropbox) of a user's Core Data persisted data. For a restore I pull the files down from Dropbox and store them temporarily in the Documents directory. Then I create a new NSPersistentContainer and use it to replace the current persistent store (in the ApplicationSupport directory) before deleting the no longer needed files in the Documents directory.

我现在正在使用MasterDetail模板,因此我具有通常的带有时间戳的实体以及随附的 NSFetchedResultsController 那。完成替换后,我交换回了主tableView,很遗憾看到了原始的实体集。当应用重新启动时,我会按预期看到还原的数据,但需要弄清楚如何使 NSFetchedResultsController 自动看到新的还原数据。

I'm using the MasterDetail template for now and so I have the usual time-stamped entities and the NSFetchedResultsController that comes along with that. Having made the replacement I swap back to the master tableView and unfortunately see the original set of entities. When the app restarts I see the restored data as intended but need to work out how to make the NSFetchedResultsController see the new restored data automatically.

这是我的还原代码:

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 问题吗?在访问新的数据存储之前,是否需要刷新?

Instead of creating a new NSPersistentContainer I have tried using dependency injection to reference the one created in the AppDelegate and using that to perform the swap but it fails trying to replace the persistent stores. Could it be some kind of NSManagedObjectContext issue? Could it need flushing prior to accessing the new data store?

推荐答案

我设法从多个回答类似问题的方法,特别是汤姆·哈灵顿。简而言之,我需要做的是:

I've managed to pull an answer together from a number of answers to similar questions, in particular this one from Tom Harrington. In short what I needed to do was:


  1. 创建一个新的 NSPersistentContainer

  2. 使用 persistentStoreCoordinator 上的 replacePersistentStore 方法将当前的持久性存储替换为还原/备份持久性存储。 code>。

  3. 销毁备份存储。

  4. 删除与备份存储关联的文件。

  5. AppDelegate 中重建核心数据堆栈。

  6. 保存,nil,然后重新初始化MasterViewController的 managedObjectContext NSFetchedResultsController

  1. Create a new NSPersistentContainer.
  2. Replace the current persistent store with the restore/backup persistent store using the replacePersistentStore method on the persistentStoreCoordinator.
  3. Destroy the backup store.
  4. Delete the files associated with the backup store.
  5. Rebuild the Core Data stack in the AppDelegate.
  6. Save, nil and then reinitialise the MasterViewController's managedObjectContext and NSFetchedResultsController.

6号花了我一些时间才能看到光。我最后的还原方法是:

Number 6 took some time for me to see the light. My final restore method is:

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)
    }
}

这篇关于核心数据。如何交换NSPersistentStores并通知NSFetchedResultsController?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆