user1698875 user1698875 - 2 months ago 21
Swift Question

Swift core data, crash on saving from alert view

I have an app that worked well with Objective C and am now updating it to Swift 3. I have an alert view to add to a table, the app crashes on

let newGauge = Gauge(context: self.coreDataStack.context)
.

I attach the Core Data Stack and the AppDelegate, my segues are working fine and passing the coreDataStack form view to view. I am probably missing the glaringly obvious, but cannot see it. help would be welcome.

AppDelegate.swift:

//
// AppDelegate.swift
// transectraingauges
//
// Created by Hugh Chare on 9/27/16.
// Copyright © 2016 Hugh Chare. All rights reserved.
//

import UIKit
import CoreData
import Foundation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
lazy var coreDataStack = CoreDataStack()

private func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {

let navigationController = window!.rootViewController as! UINavigationController
let listViewController = navigationController.topViewController as! ViewController
listViewController.coreDataStack = coreDataStack

return true
}

private func applicationWillResignActive(application: UIApplication) {
}

private func applicationDidEnterBackground(application: UIApplication) {
}

private func applicationWillEnterForeground(application: UIApplication) {
}

private func applicationDidBecomeActive(application: UIApplication) {
}

private func applicationWillTerminate(application: UIApplication) {
coreDataStack.saveContext()
}
}


CoreDataStack.swift:

//
// CoreDataStack.swift
// transectraingauges
//
// Created by Hugh Chare on 1/27/15.
// Copyright (c) 2015 Hugh Chare. All rights reserved.
//

import CoreData

class CoreDataStack {

let modelName = "RainGauges"

lazy var context: NSManagedObjectContext = {

var managedObjectContext = NSManagedObjectContext(
concurrencyType: .mainQueueConcurrencyType)

managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()

private lazy var psc: NSPersistentStoreCoordinator = {

let coordinator = NSPersistentStoreCoordinator(
managedObjectModel: self.managedObjectModel)

let url = self.applicationDocumentsDirectory
.appendingPathComponent("RainGauges.sqlite")

do {
let options =
[NSMigratePersistentStoresAutomaticallyOption : true]

try coordinator.addPersistentStore(
ofType: NSSQLiteStoreType, configurationName: nil, at: url,
options: options)
} catch {
print("Error adding persistent store.")
}

return coordinator
}()

private lazy var managedObjectModel: NSManagedObjectModel = {

let modelURL = Bundle.main
.url(forResource: self.modelName,
withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()

private lazy var applicationDocumentsDirectory: NSURL = {
let urls = FileManager.default.urls(
for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1] as NSURL
}()

func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
}


GaugeTableViewController.swift

//
// GaugeTableViewController.swift
// transectraingauges
//
// Created by Hugh Chare on 9/27/16.
// Copyright © 2016 Hugh Chare. All rights reserved.
//

import UIKit
import CoreData

class GaugeTableViewController: UIViewController, NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate {

@IBOutlet var tableView:UITableView!

var newGaugeTextField: UITextField!
var newGaugeName: String!

var editedGauge: Gauge!

var coreDataStack: CoreDataStack!
var context: NSManagedObjectContext!
var fetchedResultController:NSFetchedResultsController<Gauge> = NSFetchedResultsController()

override func viewDidLoad() {

super.viewDidLoad()

}


plus standard code for fetched results controller, etc,

@IBAction func addGauge(sender: AnyObject) {
let alert = UIAlertController(title: "New Gauge", message: "Add new gauge", preferredStyle: UIAlertControllerStyle.alert)

let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: {(action: UIAlertAction!) in

})

let saveAction = UIAlertAction(title: "Save", style: .default, handler: {(action: UIAlertAction!) in
self.newGaugeTextField = alert.textFields![0]

if self.newGaugeTextField != nil {
self.newGaugeName = self.newGaugeTextField.text}

else {
self.newGaugeName = "Unknown"
}

let newGauge = Gauge(context: self.coreDataStack.context)
newGauge.gauge = self.newGaugeName

self.tableView.reloadData()

})

alert.addTextField {
(textField: UITextField!) in
}

alert.addAction(cancelAction)
alert.addAction(saveAction)

self.present(alert, animated: true, completion: nil)
}

Answer

It looks like you haven't set the variable coreDataStack. When declare a variable like

var coreDataStack: CoreDataStack!

The exclamation point means it is an implicitly unwrapped optional. Basically it won't flag up any compiler errors because you have stated that it will be nil on creation but also that you will definitely set it before it is used. That is why you are having a crash on

let newGauge = Gauge(context: self.coreDataStack.context)

Because coreDataStack is still nil.

To fix this either initialise coreDataStack when you declare it

var coreDataStack = CoreDataStack()

or pass a CoreDataStack instance along using prepareForSegue or something like that. Just make sure the variable is initialised before you access it.

Hope this helps! :)