I want to initialize managedObject, but I always get this error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
The ViewController I want to use Core Data / managedObject is not my launching ViewController, but an other one (PortfolioVC) I am accessing through my launching ViewController.
This is my code:
import Foundation
import CoreData
class DatabaseController {
private let modelName: String
init(modelName: String) {
self.modelName = modelName
}
lazy var managedContext: NSManagedObjectContext = {
return self.storeContainer.viewContext
}()
private lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: self.modelName)
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
}
return container
}()
func saveContext () {
guard managedContext.hasChanges else { return }
do {
try managedContext.save()
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}
}
}
This is my AppDelegate:
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
lazy var coreDataStack = DatabaseController(modelName: "Portfolio")
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
guard let navController = window?.rootViewController as? UINavigationController,
let viewController = navController.topViewController as? PortfolioVC else {
return true
}
viewController.managedContext = coreDataStack.managedContext
return true
}
func applicationWillResignActive(_ application: UIApplication) {
}
func applicationDidEnterBackground(_ application: UIApplication) {
coreDataStack.saveContext()
}
func applicationWillEnterForeground(_ application: UIApplication) {
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
coreDataStack.saveContext()
}
}
This is the code in PortfolioVC:
import UIKit import CoreData
class PortfolioVC: UIViewController {
var managedContext: NSManagedObjectContext!
var namePortfolio: NamePortfolio?
override func viewDidLoad() {
super.viewDidLoad()
tableViewPortfolio.dataSource = self
let standardPortfolio = "Watchlist"
let fetchRequest: NSFetchRequest<NamePortfolio> = NamePortfolio.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(NamePortfolio.name), standardPortfolio)
do {
let results = try managedContext.fetch(fetchRequest) // here I get the error (managedContext = nil)
if results.count > 0 {
// Portfolio already created
namePortfolio = results.first
} else {
// no Portfolio
namePortfolio = NamePortfolio(context: managedContext)
namePortfolio?.name = standardPortfolio
try managedContext.save()
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
Thanks a lot for the help!
When the code in didFinishLaunchingWithOptions
runs, the PortfolioVC has not yet been added to the navigation controller stack (or even instantiated), so the guard fails. There are two ways to get the managedContext to the PortfolioVC:
a) pass it to your launching view controller in didFinishLaunchingWithOptions, then pass it from there to the PortfolioVC using prepareForSegue. You will need to add a managedContext property to your launching VC, even though you only need it to pass on to the Portfolio VC.
b) in PortfolioVC, get a reference to the AppDelegate and then get the managedContext from there.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
managedContext = appDelegate.coreDataStack.managedContext
}
Opinions vary as to which of these is the better design pattern.