2

I want to use Core Data in my iOS app, which uses UINavigationController and the first view controller within it on storyboard. And then I want to pass over the NSManagedObjectContext and NSPersistentStoreCoordinator in the AppDelegate.h to the first view controller within UINavigationController. So I first wrote the following code (note that I also use UISplitViewController):

var splitViewController = self.window!.rootViewController as UISplitViewController
var navigationController: UINavigationController!
if splitViewController.viewControllers.count == 2 {
    navigationController = splitViewController.viewControllers[1] as UINavigationController
} else {
    navigationController = splitViewController.viewControllers[0] as UINavigationController
}
var firstViewController = navigationController.topViewController
firstViewController.managedObjectContext = managedObjectContext

However, the compiler says that UIViewController doesn't have such properties as managedObjectContext. But it's weird given that when I tried to log it by println(firstViewController), it said it's an instance of FirstViewController, not UIViewController... But anyway, I changed it to the following by downcasting it:

var firstViewController = navigationController.topViewController as FirstViewController

However, then the build works properly, but it immediately is crashed by the error: "Swift dynamic cast failed" in the runtime.

So how can I pass over the NSManagedObjectContext (and NSPersistentStoreCoordinator) to the first view controller?

I use Xcode 6.1 Beta 2 and Swift in my iOS (iPad) application.

Blaszard
  • 30,954
  • 51
  • 153
  • 233

3 Answers3

2

You could check out how Core Data is used in https://github.com/Alecrim/AlecrimCoreData. It looks as if it is a well thought out Core Data library for Swift.

Yer00n
  • 123
  • 1
  • 8
1

You should define a separate class for Core Data and use it for core data stuff. Here is an example class:

class CoreDataHelper {

// MARK: - Properties

    private lazy var applicationDocumentsDirectory: NSURL = {
        let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
        return urls[urls.count - 1] as NSURL
    }()

    lazy var managedObjectModel: NSManagedObjectModel? = {
        if let modelURL = NSBundle.mainBundle().URLForResource("YOUR_MODEL_NAME", withExtension: "momd") {
            return NSManagedObjectModel(contentsOfURL: modelURL)
        } else {
            println("Error: can't create managed object model")
            return nil
        }
    }()

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
        if let managedObjectModel = self.managedObjectModel {
            let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
            let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("YOUR_MODEL_NAME.sqlite")

            var error: NSError? = nil

            let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
            coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options, error: &error)


            if let error = error {
                println("Error: Can't add persistent store type to persistent store coordinate")
                return nil
            } else {
                return coordinator
            }
        } else {
            println("Error: Can't create persistant store coordinator. Because managed object model is not valid.")
            return nil
        }
    }()

    lazy var managedObjectContext: NSManagedObjectContext? = {
        if let coordinator = self.persistentStoreCoordinator {
            let managedObjectContext = NSManagedObjectContext()
            managedObjectContext.persistentStoreCoordinator = coordinator

            return managedObjectContext
        } else {
            println("Error: Can't create managed object context. Persistent store coordinator is not valid")
            return nil
        }
    }()

}

mustafa
  • 15,254
  • 10
  • 48
  • 57
1

I just created a Master-Detail Application in Xcode 6.0 and checked the include CoreData box. The code in the template does exactly what you want...

Your first block of code:

var firstViewController = navigationController.topViewController
firstViewController.managedObjectContext = managedObjectContext

Doesn't work because at compile time, topViewController is a UIViewController, even if it is something more specific at runtime. That is why you need the cast.

As for why your downcast isn't working... Either navigationController is empty, or its topViewController is empty. More likely the former than the latter.

Try wrapping your last few lines in some if lets and see what happens:

if let nav = navigationController {
    if let firstViewController = navigationController.topViewController as? FirstViewController {
        firstViewController.managedObjectContext = managedObjectContext
    }
    else {
        println("navigationController.topViewController is not a FirstViewController")
    }
}
else {
    println("navigationController is empty")
}
Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • Both `navigationController` and `topViewController` are not empty, but it returned `navigationController.topViewController is not a FirstViewController`. So it failed to downcast `topViewController` to `FirstViewController`, but I'm not sure why. – Blaszard Oct 30 '14 at 06:57