CoreDataStack returning Nil

I’ve been restructuring my 1st Core Data App to use the approach from the book. I have created the CoreDataStack.swift, updated my AppDelegate… And my MasterViewController

(the master view controller shows me a list of all the records in CoreData - which at the time is empty).
I have an Add button via a Prepare for Seque

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
    if let addVC = segue.destination as? AddRecipientViewController {
        addVC.coreDataStack = coreDataStack
        addVC.previousVC = self
    }
    
}

In the AddRecipientViewController after user fills in a screen and presses the “Add” button.
@IBAction func addCard(_ sender: Any) {

        if let firstNameText = firstName.text {
            currentRecipient = Recipient(context: managedContext)
            currentRecipient?.firstName = firstNameText
            currentRecipient?.lastName = lastName.text!
            currentRecipient?.addressLine1 = addressLine1.text!
            currentRecipient?.addressLine2 = addressLine2.text!
            currentRecipient?.state = state.text!
            currentRecipient?.city = city.text!
            currentRecipient?.zip = zipCode.text!
            currentRecipient?.country = country.text!
        }
    do {
        try managedContext.save()
    } catch let error as NSError {
        print("Save error: \(error), \(error.userInfo)")
    }
        navigationController?.popViewController(animated: true)
}

The program crashes at the Recipient(context: managedContext), with the message:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
The “nil” it is finding, is in the coreDataStack

I’ve been scratching my head most of this weekend. Any suggestions how to figure this out? I have confirmed that it is nil during the prepare for segue.

but since the database has no value in it… should the Add button create the context?

Adding my CoreDataStack in case this helps:

import Foundation

import CoreData

class CoreDataStack{

private let modelName: String

init(modelName: String) {
    self.modelName = modelName
}

lazy var managedContext: NSManagedObjectContext = {
    return self.persistentContainer.viewContext
}()

private lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: modelName)
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            print("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

func saveContext () {
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.save()
        } catch {
            let nserror = error as NSError
            print("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}

}

And the top of the MasterViewController.swift where I define the var’s

var managedContext: NSManagedObjectContext!
var coreDataStack: CoreDataStack!

var recipients : [Recipient] = []
var selectedRecipient: Recipient?

override func viewDidLoad() {
    super.viewDidLoad()

}

func getRecipients() {
    
    if let context = coreDataStack {
    
        if let coreDataCards = try? context.managedContext.fetch(Recipient.fetchRequest()) as? [Recipient] {
            if let recipient = coreDataCards {
                recipients = recipient
                tableView.reloadData()
            }
        }
    }
    
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    getRecipients()
}

If I remove the if let context = coreDataStack { } wrap around the if let coreDataCards = try? then I actually get a crash a the if let coreDataCards = try? - with:

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

There are currently no records in the Database…

OK… Figured out what I was missing, I was not passing the managedContext between view controllers. Should I be passing it or should I declare it as a global variable?

The approach in the core data book is to create coreDataStack in the app delegate, pass it to the root or master view controller, and have it pass it along to any other view controllers. Your first post seems to show that happening with the addVC controller.

Then each controller uses coreDataStack.managedContext anywhere it needs to get to the managed context. You don’t use a separate managedContext variable.

So in that first post, for instance, you would create the new recipient with

currentRecipient = Recipient(context: coreDataStack.managedContext)
1 Like

@theapapp You definitely pass it between View Controllers and should avoid using a global variable (i.e. via a Singleton). Also remember that you should pass it between View Controllers even if the immediate View Controller is NOT in need of it. For example:

Your app has 3 View Controllers, let’s call them A, B, C. If A uses the MOC, B does not, and C does. What do you do? How do you get the same reference of the MOC over to C from A? Simple, A gives it to B, which gives it to C. Even though B is not using it, you should still pass it to B, so that it can send it to C where it will be used.

I hope this helps!

All the best!

2 Likes

This topic was automatically closed after 166 days. New replies are no longer allowed.