核心数据。如何交换NSPersistentStores并通知NSFetchedResultsController? [英] Core Data. How to swap NSPersistentStores and inform 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:
- 创建一个新的
NSPersistentContainer
。 - 使用
persistentStoreCoordinator 上的
replacePersistentStore
方法将当前的持久性存储替换为还原/备份持久性存储。 code>。 - 销毁备份存储。
- 删除与备份存储关联的文件。
- 在
AppDelegate
中重建核心数据
堆栈。 - 保存,nil,然后重新初始化MasterViewController的
managedObjectContext
和NSFetchedResultsController
。
- Create a new
NSPersistentContainer
. - Replace the current persistent store with the restore/backup persistent store using the
replacePersistentStore
method on thepersistentStoreCoordinator
. - Destroy the backup store.
- Delete the files associated with the backup store.
- Rebuild the
Core Data
stack in theAppDelegate
. - Save, nil and then reinitialise the MasterViewController's
managedObjectContext
andNSFetchedResultsController
.
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屋!