I have a fairly standard Core Data fed tableView with cell data populated from a fetchedResultsController.
Everything works as expected until I do a Core Data migration. The purpose of the lightweight migration is to provide a simple backup not to change the model. The store uses SQLite. The plan is to do the migration to generate the new data files and then to remove the new store and install the original store in order to keep the original file names.
The view for the backup procedure is also a tableView. Once the migration is completed, the new file is visible in the backup tableView. Upon clicking the "back" button to return to the original tableView, the data is visible as expected, but clicking on any row in the tableView causes an immediate crash and I'm presented with the dreaded "Object's persistent store is not reachable from this NSManagedObjectContext's coordinator" error.
I've been struggling with this for a week. I must be missing a basic concept. Any help would be appreciated. (iOS 8, Xcode 6.4)
Here are the fetchedResultsController variables. Again these work all the time until a migration is made:
var myFetchedResultsController: NSFetchedResultsController? = nil
var fetchedResultsController: NSFetchedResultsController {
managedObjectContext = kAppDelegate.managedObjectContext
if myFetchedResultsController != nil {
return myFetchedResultsController!
}//if my ! nil
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Patient", inManagedObjectContext: managedObjectContext)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 50
//Sort keys
let sortDescriptor = NSSortDescriptor(key: "dateEntered", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
var countError : NSError? = nil
var count = managedObjectContext.countForFetchRequest(fetchRequest, error: &countError)
println("The count is \(count)")
//after creating a backup, this count is ALWAYS zero - never the real count
aFetchedResultsController.delegate = self
myFetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !myFetchedResultsController!.performFetch(&error) {
// Don't forget the code to handle the error appropriately.
println("Unresolved error \(error), \(error!.userInfo)")
//Remove this
abort()
}//if !my
return myFetchedResultsController!
}//var fetchedResultsController
The two functions for the backup procedure:
func createLocalBackupFile() {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyyMMddHHmmss"
let theDateTime = NSDate()
let formattedDateTime = dateFormatter.stringFromDate(theDateTime)
let backupFileName : String = "BiopBak" + formattedDateTime + ".sqlite"
println("backupFileName is \(backupFileName)")
let psu : CRSPersistentStoreUtilities = CRSPersistentStoreUtilities()//the function below is in this class
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
//println("In a background queue, creating the backup file")
psu.backupTheStore(backupFileName)
//go back to the main queue
dispatch_async(dispatch_get_main_queue(), { () -> Void in
println("Back on main queue after creating the backup file")
if (self.backupSqlFiles.count == 1 && self.backupSqlFiles[0] == "Placeholder for empty list") {
self.backupSqlFiles.append(backupFileName.stringByDeletingPathExtension)
self.backupSqlFiles.removeAtIndex(0)
} else {
self.backupSqlFiles.append(backupFileName.stringByDeletingPathExtension)
}//if placeholder is only record in database - else
self.tableView.reloadData()
println("backupSqlFiles[] = \(self.backupSqlFiles)")
})//back to main block - inner
})//background processing block - outer
}//createLocalBackupFile
func backupTheStore(newSQLFileName : String) -> NSPersistentStore? {
let storeType = NSSQLiteStoreType
var migrateError : NSError?
var currentStore : NSPersistentStore = kAppDelegate.persistentStoreCoordinator?.persistentStores.last! as! NSPersistentStore
let options = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
let fileManager = NSFileManager.defaultManager()
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsDir = paths[0] as! String
let docsDirURL = NSURL(fileURLWithPath: docsDir)
let originalStoreURL : NSURL = docsDirURL?.URLByAppendingPathComponent("BiopLogCloud.sqlite") as NSURL!
var newStoreURL : NSURL = docsDirURL?.URLByAppendingPathComponent(newSQLFileName) as NSURL!
kAppDelegate.persistentStoreCoordinator?.migratePersistentStore(currentStore, toURL: newStoreURL, options: options, withType: storeType, error: &migrateError)
currentStore = kAppDelegate.persistentStoreCoordinator?.persistentStores.last! as! NSPersistentStore
var removeStoreError : NSError?
var theStores = kAppDelegate.persistentStoreCoordinator?.persistentStores
if let theStores2 = theStores {
for removeStore in theStores2 {
var removed : Bool = true
kAppDelegate.persistentStoreCoordinator?.removePersistentStore(removeStore as! NSPersistentStore, error: &removeStoreError)
if (removeStoreError != nil) {
println("Unable to remove persistent store \(removeStore)")
}
}//for in
}//if let theStores
var addStoreError : NSError?
kAppDelegate.persistentStoreCoordinator?.addPersistentStoreWithType(storeType,
configuration: nil,
URL: originalStoreURL,
options: options,
error:&addStoreError)
if (addStoreError != nil) {
println("Unable to add persistent store \(originalStoreURL)")
//change this to add a user alert
}//if
//this does not seem to do any good
let ptvc : PatientTableViewController = PatientTableViewController()
dispatch_async(dispatch_get_main_queue()) {
() -> Void in
ptvc.tableView.reloadData()
}//block
return thisStore
}//backupTheStore