2016-05-19 9 views
2

Ich versuche, den Core Data Data Stack neu zu erstellen, wenn eine Lightweight-Migration fehlgeschlagen ist, und den Benutzer zurück zum Anmeldebildschirm zu senden. Ich teste das, indem ich eine Zu-Viele-Beziehung zu einer Eins-zu-Eins-Beziehung ändere.Fehler beim Neuaufbau/Zurücksetzen von Core-Daten

Zuerst verwendete ich die gleiche URL (storeURL), wenn ich den neuen persistentStoreCoordinator nach dem Entfernen hinzufügte; Ich habe jedoch eine Fehlermeldung "Kann nicht denselben Speicher zweimal hinzufügen" in rebuildCoreData() in der Zeile "versuchen Sie persistantStoreCoordinator.add ..."

Zweitens habe ich beschlossen, die URL im neuen persistenten Speicher zu ändern durch Anhängen einer "1", so dass es selb.applicationDocumentsDirectory.URLByAppendingPathComponent ("SingleViewCoreData1.sqlite") wurde. Dies hat einen Schritt in die richtige Richtung gemacht - noch keine Fehler, und ich bin in der Lage, auf den Login-Bildschirm zurückzukehren. Nach dem Versuch, den ersten Speichervorgang nach der Anmeldung durchzuführen, erhalte ich jedoch die Fehlermeldung 'Dieser NSPersistentStoreCoordinator hat keine persistenten Speicher (Schema-Mismatch oder Migrationsfehler). Es kann keine Speicheroperation ausgeführt werden.

Was mache ich hier falsch?

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { 
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. 
    // Create the coordinator and store 
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") 
    let options = [NSMigratePersistentStoresAutomaticallyOption: true, 
        NSInferMappingModelAutomaticallyOption: true] 
    var failureReason = "There was an error creating or loading the application's saved data." 
    do { 
     try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options) 
    } catch { 
     // Report any error we got. 
     var dict = [String: AnyObject]() 
     dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" 
     dict[NSLocalizedFailureReasonErrorKey] = failureReason 

     dict[NSUnderlyingErrorKey] = error as NSError 
     let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 
     NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") 
     self.rebuildCoreData() 
    } 

    return coordinator 
}() 

lazy var managedObjectContext: NSManagedObjectContext = { 
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. 
    let coordinator = self.persistentStoreCoordinator 
    var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) 
    managedObjectContext.persistentStoreCoordinator = coordinator 
    managedObjectContext.mergePolicy = NSRollbackMergePolicy //This policy discards in-memory state changes for objects in conflict. The persistent store’s version of the objects’ state is used 

    return managedObjectContext 
}() 

// MARK: - Tearing down core data stack and rebuilding it in the case that a lightweight migration fails 
func rebuildCoreData() { 

    let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") 
    do { 
     try NSFileManager.defaultManager().removeItemAtURL(storeURL) 
    } catch { 
     print(error) 
     abort() 
    } 

    for object in managedObjectContext.registeredObjects { 
     managedObjectContext.deleteObject(object) 
    } 

    do { 
     try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: [NSMigratePersistentStoresAutomaticallyOption: true, 
      NSInferMappingModelAutomaticallyOption: true]) 
    } catch { 
     print(error) 
     abort() 
    } 

    print("successfully rebuilt core data") 
    let storyboard = UIStoryboard(name: "Main", bundle: nil) 
    let controller = storyboard.instantiateInitialViewController() 
    self.window?.rootViewController?.presentViewController(controller!, animated: false, completion: nil) 
} 

UPDATE - Geänderte Löschen von Dateien und bearbeitet persistenten Speicher Koordinator Logik im catch-Block

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { 
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. 
    // Create the coordinator and store 
    var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") 
    let options = [NSMigratePersistentStoresAutomaticallyOption: true, 
        NSInferMappingModelAutomaticallyOption: true] 
    var failureReason = "There was an error creating or loading the application's saved data." 
    do { 
     try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options) 
    } catch { 

     // Report any error we got. 
     var dict = [String: AnyObject]() 
     dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" 
     dict[NSLocalizedFailureReasonErrorKey] = failureReason 

     dict[NSUnderlyingErrorKey] = error as NSError 
     let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 
     NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") 

     //rebuilds core data 
     coordinator = self.rebuildCoreData(coordinator) 
    } 

    return coordinator 
}() 

New rebuildCoreData Code:

// MARK: - Tearing down core data stack and rebuilding it in the case that a lightweight migration fails 
func rebuildCoreData(coordinator: NSPersistentStoreCoordinator) -> NSPersistentStoreCoordinator { 

    let persistentStoreParentPath = self.applicationDocumentsDirectory.path 
    let fileEnumerator = NSFileManager.defaultManager().enumeratorAtPath(persistentStoreParentPath!) 
    while let path = fileEnumerator?.nextObject() as? String { 
     if path.hasPrefix("SingleViewCoreData.sqlite") || path.hasPrefix(".SingleViewCoreData.sqlite") { 
      let pathToDelete = (persistentStoreParentPath! as NSString).stringByAppendingPathComponent(path) 
      do { 
       try NSFileManager.defaultManager().removeItemAtPath(pathToDelete) 
      } 
      catch _ { 
       // Handle error removing file 
      } 
     } 
    } 

    for object in managedObjectContext.registeredObjects { 
     managedObjectContext.deleteObject(object) 
    } 

    do { 
     let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") 
     try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: [NSMigratePersistentStoresAutomaticallyOption: true, 
      NSInferMappingModelAutomaticallyOption: true]) 
    } catch { 
     print(error) 
     abort() 
    } 

    print("successfully rebuilt core data") 
    let storyboard = UIStoryboard(name: "Main", bundle: nil) 
    let controller = storyboard.instantiateInitialViewController() 
    self.window?.rootViewController?.presentViewController(controller!, animated: false, completion: nil) 

    return coordinator 
} 

Antwort

3

Der CoreData Stapel besteht nicht aus einer einzigen Datei in neueren iOS-Versionen. Sie können Ihren CoreData Stapel erstellen und das Dateisystem überprüfen, um speziell zu sehen, wie die Dateien benannt sind (es gibt eine zusätzliche .shm und eine .wal Datei, wie ich mich erinnere). Nachdem ich das gesagt habe, verwende ich normalerweise NSFileManager, um die Objekte im übergeordneten Pfad meiner Geschäftsdateien aufzuzählen und alles mit dem Präfix <StoreFileName>.sqlite und .<StoreFileName>.sqlite zu entfernen. Sie könnten auch in Erwägung ziehen, Ihre Geschäftsdateien in ein Unterverzeichnis zu verschieben und dann einfach das gesamte Unterverzeichnis zu löschen.

let persistentStoreParentPath = self.applicationDocumentsDirectory.path 
let fileEnumerator = NSFileManager.defaultManager().enumeratorAtPath(persistentStoreParentPath) 
while let path = fileEnumerator?.nextObject() as? String { 
    if path.hasPrefix("SingleViewCoreData.sqlite") || path.hasPrefix(".SingleViewCoreData.sqlite") { 
     let pathToDelete = (persistentStoreParentPath as NSString).stringByAppendingPathComponent(path) 
     do { 
      try NSFileManager.defaultManager().removeItemAtPath(pathToDelete) 
     } 
     catch _ { 
      // Handle error removing file 
     } 
    } 
} 

Ihr Code oben hat jedoch einige andere Probleme zu berücksichtigen. Sie rufen rebuildCoreData() von innerhalb der Schließung, die persistentStoreCoordinator initialisiert, aber Sie verwenden persistentStoreCoordinator in rebuildCoreData() sowohl direkt als auch indirekt (über den Zugriff auf managedObjectContext, die persistentStoreCoordinator verwendet).

+0

Können Sie bitte ein Beispielcode-Snippet bereitstellen? Und was ist mit dem zweiten Fehler, den ich bekam, wo NSPersistentStoreCoordinator keine persistenten Speicher hat? –

+0

@mckamike Möchten Sie ein Beispiel zum Aufzählen und Löschen von Dateien? Nachdem Ihre 'CoreData'-Dateien gelöscht wurden, würde ich einfach den gesamten' CoreData'-Stack neu erstellen. Sie sparen nicht viel, indem Sie den zuvor erstellten Koordinator beibehalten. –

+0

Ja zu Ihrer Frage, und zu Ihrer zweiten Aussage, wie kann ich sie "neu erstellen"? –