I have a class (Swift 3) in my app that contains the basic CoreData stack which I moved out of AppDelegate
. All of the operations the app needs to perform are in another class I call CoreDataHelper
. When the app is launched, AppDelegate creates an instance of CoreDataHelper (let dataManager = CoreDataHelper()
) which is the only thing that talks to the stack. Then in launchCalculatorViewController()
, dataManager
asks the stack for a new LiftEvent
:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let dataManager = CoreDataHelper()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// check if data needs to be loaded and load it if needed...
launchCalculatorViewController()
return true
}
func launchCalculatorViewController() {
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let initialViewController: CalculatorViewController = mainStoryboard.instantiateInitialViewController() as? CalculatorViewController {
// create the liftEvent, the viewModel, and wire it up to the view controller
let liftEvent = dataManager.createNewLiftEvent() // go get me a new LiftEvent
let viewModel = calculatorLiftEventViewModelFromLiftEvent(withLiftEvent: liftEvent, dataManager: dataManager)
initialViewController.viewModel = viewModel
initialViewController.dataManager = dataManager
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
}
here's the createNewLiftEvent()
method in dataManager
:
func createNewLiftEvent() -> LiftEvent {
let newLiftEvent = LiftEvent(dataManager: self, insertIntoManagedObjectContext: stack.managedObjectContext)
return newLiftEvent
}
and the initialization of LiftEvent
looks like this:
class LiftEvent: NSManagedObject, LiftEventProtocol {
var dataManager: CoreDataHelper!
override func awakeFromInsert() {
super.awakeFromInsert()
let date = Date()
self.date = date
let defaultUnit = UserDefaults.weightUnit()
if defaultUnit == "kg" {
weight = Measurement(value: 0.0, unit: .kilograms)
liftWeights[defaultUnit] = weight
} else {
weight = Measurement(value: 0.0, unit: .pounds)
liftWeights[defaultUnit] = weight
}
if let defaultFormula = dataManager.fetchDefaultFormula() { // unexpected nil error thrown here
self.formula = defaultFormula // this is an NSManagedObject
}
}
convenience init(dataManager: CoreDataHelper, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
let entity = NSEntityDescription.entity(forEntityName: "LiftEvent", in: context)!
self.init(entity: entity, insertInto: context) // exits after this line
self.dataManager = dataManager // never gets executed so of course it's nil
}
}
I get the "unexpectedly found nil while unwrapping an Option value" error because dataManager
is nil at this point. I try to set it in the convenience initializer but self.dataManager = dataManager
is never executed because it exits right after self.init
. Of course I can't put it before self.init
because the object has to exist before I can set the value.
I'm passing in dataManager
in an attempt to ensure that the Formula (also an NSManagedObject
) and this new LiftEvent
being created are in the same managed object context so I can set the relationship between them in awakeFromInsert()
. Before trying this approach it was crashing because the LiftEvent
and Formula
were it two different managedObjectContexts
(even though I did a lot of looking to make sure that dataManager
and the stack were only instantiated once). The approach above was inspired by this thread, but I think I might not understand initialization as well as I thought I did.
How do I pass in the managed object context to create a LiftEvent
so it's in the same managed object context as Formula
so the relationship can be set? Or, is the totally the wrong approach for this?