1

I been doing

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

and just use the context in my ViewControllers but in Apples iOS developer Library I read

"A view controller typically shouldn’t retrieve the context from a global object such as the application delegate"

so what is the right way to access the context from the AppDelegate, I am a little bit confused...

Eric Yu
  • 213
  • 2
  • 12
  • @Wain The duplicate question doesn't have an example solution with code. What to do then, should I move my answer there? – pedrouan Aug 30 '16 at 19:48
  • you could, your answer was a simple link when i marked this as a duplicate, but there is no point in this question / answer as it is the same, also it's the theory that's important - adding the code just serves to make people copy&paste without thinking in a lot of cases – Wain Aug 30 '16 at 21:08

1 Answers1

1

You are right, Apple doesn't recommend that way for iOS applications.

Creation of CoreData stack is the best solution:

So in your AppDelegate.swift:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?
  lazy var coreDataStack = CoreDataStack()

  func application(_ application: UIApplication,
    didFinishLaunchingWithOptions
    launchOptions: [NSObject: Any]?) -> Bool {

    let navigationController =
self.window!.rootViewController as! UINavigationController

    let viewController =
navigationController.topViewController as! ViewController

    viewController.managedContext = coreDataStack.context

    return true
  }

  func applicationDidEnterBackground(_ application: UIApplication) {
    coreDataStack.saveContext()
  }

  func applicationWillTerminate(_ application: UIApplication) {
    coreDataStack.saveContext()
  }

Then, an example of CoreDataStack.swift (the class that holds all the duties about the MOC):

import CoreData    
class CoreDataStack {

  lazy var context: NSManagedObjectContext = {

    var managedObjectContext = NSManagedObjectContext(
      concurrencyType: .mainQueueConcurrencyType)

    managedObjectContext.persistentStoreCoordinator = self.psc
    return managedObjectContext
    }()

  fileprivate lazy var psc: NSPersistentStoreCoordinator = {

    let coordinator = NSPersistentStoreCoordinator(
      managedObjectModel: self.managedObjectModel)

    let url = self.applicationDocumentsDirectory
      .appendingPathComponent(self.modelName)

    do {
      let options =
      [NSMigratePersistentStoresAutomaticallyOption : true]

      try coordinator.addPersistentStore(
        ofType: NSSQLiteStoreType, configurationName: nil, at: url,
        options: options)
    } catch  {
      print("Error adding persistent store.")
    }

    return coordinator
    }()

  fileprivate lazy var managedObjectModel: NSManagedObjectModel = {

    let modelURL = Bundle.main
      .url(forResource: self.modelName,
        withExtension: "momd")!
    return NSManagedObjectModel(contentsOf: modelURL)!
    }()

  fileprivate lazy var applicationDocumentsDirectory: URL = {
    let urls = FileManager.default.urls(
      for: .documentDirectory, in: .userDomainMask)
    return urls[urls.count-1]
    }()

  func saveContext () {
    if context.hasChanges {
      do {
        try context.save()
      } catch let error as NSError {
        print("Error: \(error.localizedDescription)")
        abort()
      }
    }
  }
}

and finally an example of ViewController.swift:

import CoreData    
class ViewController: UIViewController {

  var managedContext: NSManagedObjectContext!      
  override func viewDidLoad() {        

    let myEntity = NSEntityDescription.entity(forEntityName: "MyEntityName",
      in: managedContext)

    ...

  }

}
pedrouan
  • 12,762
  • 3
  • 58
  • 74
  • wait...so, I should not used the core data stack that was automatically generated by xcode ? how is this different than – Eric Yu Aug 30 '16 at 19:54
  • Yes, as cited 'Ideally, each view controller should be an island on its own. It should not rely on its parent, nor should it rely on the Application Delegate. Once a view controller is pushed onto the screen it should ideally be its own master.' Their docs are bit messy. Here is more information: http://www.cimgf.com/2011/01/07/passing-around-a-nsmanagedobjectcontext-on-the-iphone/ – pedrouan Aug 30 '16 at 20:06
  • Thanks for the link and the example code it is very helpful – Eric Yu Aug 30 '16 at 22:50
  • So did my answer help you? :) – pedrouan Oct 01 '16 at 14:01
  • yes very helpfully. Thank you. But after the changes I decided to change back my original method. reason being 1. I am not reusing my codes. 2. my project is simply enough that, doing it the "right" way actually complicate things a little bit more. – Eric Yu Oct 05 '16 at 17:05
  • so I should send a managedContext to another view controller when I move to another view controller (pushViewController or present) if that view controller have a task that write/read data from coredata? (sorry my bad english laguage) – Jan sebastian Apr 01 '21 at 09:29