2016-06-22 43 views
43

Di fronte a tema Valore" di tipo 'AppDelegate' ha nessun membro 'managedObjectContext' Nel nuovo Xcode 8 (utilizzando Swift 3, iOS 10) quando si tenta di creare nuovo contesto in View ControllerCome creare managedObjectContext utilizzando Swift 3 in Xcode 8?

let context = (UIApplication.shared().delegate as! AppDelegate).managedObjectContext 

In Xcode 8 c'è . nessun codice per managedObjectContext all'interno di file AppDelegate.swift core Data pila di codici all'interno AppDelegate.swift presentata solo con: pigro var persistentContainer: proprietà NSPersistentContainer e func saveContext() non v'è alcuna proprietà managedObjectContext

Come creare managedObjectContext utilizzando Swift.. 3 in Xcode 8) o forse non è necessario farlo utilizzando Swift 3?

+2

Dovresti dare un'occhiata a questo talk https://developer.apple.it/videos/play/wwdc2016/242/ – jjatie

risposta

77

In Swift3, è possibile accedere alle managedObjectContext tramite il ViewContext come

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext 

Questa opzione è disponibile se i dati Nucleo è stata abilitata durante la creazione del progetto. Tuttavia, per il progetto esistente che si desidera includere i dati di base, passare attraverso il normale processo di aggiunta dei dati di base e aggiungere il seguente codice che vi permetterà di ottenere il

lazy var persistentContainer: NSPersistentContainer = { 

    let container = NSPersistentContainer(name: "you_model_file_name") 
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in 
     if let error = error { 

      fatalError("Unresolved error \(error), \(error.userInfo)") 
     } 
    }) 
    return container 
}() 

Sarà necessario importare il CoreData.

Nota: per Swift3, la sottoclasse ManagedObject viene generata automaticamente. + info Da WWDC 2016

+19

Penso che sia importante notare che 'NSPersistentContainer' è un * iOS 10 * API e non è particolarmente legato a Swift 3. Se prevedi di supportare iOS 9.x, hai vinto ' essere in grado di usare 'NSPersistentContainer', e sarà necessario impostare lo stack di Core Data come si avrebbe prima di usare Xcode 8. – andrewcbancroft

+1

Ciao. C'è un modo altrettanto semplice per farlo per un Mac App? Poiché 'NSPersitentContainer' è iOS, l'approccio di prima linea non funziona. – DaPhil

+1

La [documentazione] (https://developer.apple.com/reference/coredata/nspersistentcontainer) indica macOS 10.12 come SDK supportato. – mic

3

NSPersistentContainer ha una proprietà viewContext che è un tipo NSManagedObjectContext.

Come nota a margine, se si crea una configurazione Master-Detail in Xcode 8, codice di esempio di Apple mette la proprietà managedObjectContext nel file MasterViewController.swift e imposta esso utilizzando detto viewContext proprietà in AppDelegate.

27

La soluzione da James Amo si ottiene la maggior parte del tragitto per iOS 10.0, ma non affronta iOS 9.0 e di seguito, che non possono accedere a tale metodo e ha bisogno di costruire manualmente la NSManagedObjectModel. Ecco la soluzione che ha funzionato per me:

var context: NSManagedObjectContext? 

    if #available(iOS 10.0, *) { 
     context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext 
    } else { 
     // iOS 9.0 and below - however you were previously handling it 
     guard let modelURL = Bundle.main.url(forResource: "Model", withExtension:"momd") else { 
      fatalError("Error loading model from bundle") 
     } 
     guard let mom = NSManagedObjectModel(contentsOf: modelURL) else { 
      fatalError("Error initializing mom from: \(modelURL)") 
     } 
     let psc = NSPersistentStoreCoordinator(managedObjectModel: mom) 
     context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) 
     let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) 
     let docURL = urls[urls.endIndex-1] 
     let storeURL = docURL.appendingPathComponent("Model.sqlite") 
     do { 
      try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil) 
     } catch { 
      fatalError("Error migrating store: \(error)") 
     } 

    } 

E 'chiaro che il cambiamento a 10,0 rende CoreData notevolmente più semplice, ma è un peccato che sia così doloroso per gli sviluppatori esistenti per fare il salto ...

Per implementa quanto sopra, assicurati di lanciare il persistentContainer nel tuo AppDelegate.swift, definito nella risposta James Amo.

+1

dove mettere questo codice? –

32

Trasferito tutto il codice dello stack di dati di base in un singolo file e aggiunto iOS 10 e sotto iOS10. Sotto è il mio tentativo (non sono sicuro la sua pienamente all'altezza del marchio)

import Foundation 
import CoreData 

class CoreDataManager { 
    // MARK: - Core Data stack 
    static let sharedInstance = CoreDataManager() 

    private lazy var applicationDocumentsDirectory: URL = { 
     // The directory the application uses to store the Core Data store file. This code uses a directory named in the application's documents Application Support directory. 
     let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) 
     return urls[urls.count-1] 
    }() 

    private lazy var managedObjectModel: NSManagedObjectModel = { 
     // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. 
     let modelURL = Bundle.main.url(forResource: "CoreDataSwift", withExtension: "momd")! 
     return NSManagedObjectModel(contentsOf: modelURL)! 
    }() 

    private 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.appendingPathComponent("CoreDataSwift.sqlite") 
     var failureReason = "There was an error creating or loading the application's saved data." 
     do { 
      // Configure automatic migration. 
      let options = [ NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true ] 
      try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options) 
     } catch { 
      // Report any error we got. 
      var dict = [String: AnyObject]() 
      dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject? 
      dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject? 

      dict[NSUnderlyingErrorKey] = error as NSError 
      let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 
      // Replace this with code to handle the error appropriately. 
      // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
      NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") 
      abort() 
     } 

     return coordinator 
    }() 

    lazy var managedObjectContext: NSManagedObjectContext = { 

     var managedObjectContext: NSManagedObjectContext? 
     if #available(iOS 10.0, *){ 

      managedObjectContext = self.persistentContainer.viewContext 
     } 
     else{ 
     // 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 
     managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) 
     managedObjectContext?.persistentStoreCoordinator = coordinator 

     } 
     return managedObjectContext! 
    }() 
    // iOS-10 
    @available(iOS 10.0, *) 
    lazy var persistentContainer: NSPersistentContainer = { 
     /* 
     The persistent container for the application. This implementation 
     creates and returns a container, having loaded 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. 
     */ 
     let container = NSPersistentContainer(name: "CoreDataSwift") 
     container.loadPersistentStores(completionHandler: { (storeDescription, error) in 
      if let error = error as NSError? { 
       // Replace this implementation with code to handle the error appropriately. 
       // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 

       /* 
       Typical reasons for an error here include: 
       * The parent directory does not exist, cannot be created, or disallows writing. 
       * The persistent store is not accessible, due to permissions or data protection when the device is locked. 
       * The device is out of space. 
       * The store could not be migrated to the current model version. 
       Check the error message to determine what the actual problem was. 
       */ 
       fatalError("Unresolved error \(error), \(error.userInfo)") 
      } 
     }) 
     print("\(self.applicationDocumentsDirectory)") 
     return container 
    }() 
    // MARK: - Core Data Saving support 

    func saveContext() { 
     if managedObjectContext.hasChanges { 
      do { 
       try managedObjectContext.save() 
      } catch { 
       // Replace this implementation with code to handle the error appropriately. 
       // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
       let nserror = error as NSError 
       NSLog("Unresolved error \(nserror), \(nserror.userInfo)") 
       abort() 
      } 
     } 
    } 
} 
+0

Penso che questo sia esattamente quello che sto cercando. Se questa classe viene semplicemente condivisa con l'app e un'estensione nello stesso gruppo (destinata a entrambe le app), ciò consentirà ai dati di essere accessibili da entrambi? Ho riscontrato questo problema con un'estensione della tastiera in cui entrambi utilizzano lo stesso "contenitore persistente" e lo stesso modello di dati, ma i dati non sono condivisi. qualche idea? –

+0

Ciao, non ne sono sicuro, ho appena creato questo:) ... nessun test approfondito. Potrebbe essere possibile provare l'approccio quadro https://www.andrewcbancroft.com/2015/08/25/sharing-a-core-data-model-with-a-swift-framework/ – anoop4real

+1

Ho finito per ottenerlo. Si aggiunge un NSPersistentStoreDescription che punta al gruppo di app durante la creazione del contenitore nella versione ios 10. –

0

In primo luogo, ottenere l'oggetto AppDelegate: -

let appDelegateObject = UIApplication.shared.delegate as! AppDelegate 

Ed oggetto ora, possiamo ottenere gestito come:

let managedObject = appDelegateObject.persistentContainer.viewContext