0

I am developing a project in XCode 8 with Core data Functionality. Xcode 8 does support Core Data functionality for iOS 10 and up but when I try to implement it for iOS 8 and 9, it crashes when getting managedContext with the message:

fatal error: unexpectedly found nil while unwrapping an Optional value

for the code:

let managedContext = appDelegate.managedObjectContext

App Delegate:

// MARK: - Core Data stack

@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: "Feel")
    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)")
        }
    })
    return container
}()


lazy var applicationDocumentsDirectory: URL = {

    let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return urls[urls.count-1]
}()

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: "coreDataTemplate", withExtension: "momd")!
    return NSManagedObjectModel(contentsOf: modelURL)!
}()

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("SingleViewCoreData.sqlite")
    var failureReason = "There was an error creating or loading the application's saved data."
    do {
        try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
    } 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 = {
    // 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
    return managedObjectContext
}()


// MARK: - Core Data Saving support

func saveContext () {
    if #available(iOS 10.0, *) {

        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                // 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.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    } else {
        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()
            }
        }
    }
}

Fetching Code:

guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else {
            return
    }

    if #available(iOS 10.0, *) {
        let managedContext =
            appDelegate.persistentContainer.viewContext

        let fetchRequest =
            NSFetchRequest<NSManagedObject>(entityName: "Test")

        do {
            tests = try managedContext.fetch(fetchRequest)
        } catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
        }
    } else {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Test")

        do {
            let results =
                try managedContext.fetch(fetchRequest)
            tests = results as! [NSManagedObject]
        } catch let error as NSError {
            print("Could not fetch \(error), \(error.userInfo)")
        }
    }

How can I implement Core Data for an iOS project with deployment version being iOS 8.2 in XCode 8 using Swift 3?

2
  • Are you sure that is the line where the error occurs? Can you print(appDelegate) right before it?
    – jtbandes
    Commented Dec 30, 2016 at 6:41
  • @jtbandes Yes, I did print appDelegate and it returns me the instance of the AppDelegate. Commented Dec 30, 2016 at 6:48

2 Answers 2

3

Are you using a different model name in the iOS 8/9 code? Could you try replacing "coreDataTemplate" with "Feel" in your AppDelegate's managedObjectModel property like this:

let modelURL = Bundle.main.url(forResource: "Feel", withExtension: "momd")!
0

I'm not sure why it's throwing this particular error, but eliminating the following line might fix it:

let appDelegate = UIApplication.shared.delegate as! AppDelegate

Note that you already have the appDelegate from the guard statement above, and all you're doing here is creating a new constant with the same name, within a lower context. So, you can easily eliminate this line. (Actually, I should think that this downcasting would fail and generate a different error.)

There are a couple other recommendations I would make.

  • First, most importantly, cut out all the code relating to the Persistent Store Coordinator. It's redundant. Instead, always use the managedObjectContext property from the app delegate. That will still work in iOS 10.

    (By using both the persistent store coordinator and the pre-iOS 10 code, you are just creating double the work for yourself. And, you're likely to get a lot more bugs, as you just did here.)

  • The other recommendation is just a matter of preferred practice. You can create a managedObjectContext property in each of the view controllers. And, in the app delegate, set all of them to the app delegate's managedObjectContext. As a matter of preferred practice, the view controllers generally should not be referring back to the app delegate.

Not the answer you're looking for? Browse other questions tagged or ask your own question.