Core Data creating nil entity entry

Hello Everyone. I am new to Core Data and I am having a strange issue when adding a new entity entry when I try to add a child entity at the same time. I am trying to create a new Project Entity and adding a Feature Entity at the same time. When I save the addition it tries to reload the table data but throws an error because there is a second Project Entity that was created with a nil projectName and projectDate. If I comment out the add of the Feature Entity everything works fine. Any ideas?


import UIKit

import CoreData

class ProjectTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {


var managedObjectContext: NSManagedObjectContext? = nil

override func viewDidLoad() {

super.viewDidLoad()

/

/

/

/

}

override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

/

}


@IBAction func addProject(_ sender: AnyObject){

let alert = UIAlertController(title: "New Project",

message: "Add a new project",

preferredStyle: .alert)

let saveAction = UIAlertAction(title: "Save",

style: .default,

handler: { (action:UIAlertAction) -> Void in

let context = self.fetchedResultsController.managedObjectContext

/

let newProject = SSProjectMO(context: context)

/

let textField = alert.textFields!.first

newProject.projectName = textField!.text!

newProject.projectDate = NSDate();

let newItem = NSEntityDescription.insertNewObject(forEntityName: "Feature", into: context) as! SSFeatureMO

newItem.featureName = "High Challenge Course"

newProject.addToProjectRelationship(newItem)

print("Saving with Date \(newProject.projectDate)")

/

do {

/

try context.save()

} catch {

/

/

print("Error Saving Project")

let nserror = error as NSError

fatalError("Unresolved error \(nserror), \(nserror.userInfo)")

}

self._fetchedResultsController = nil

self.tableView.reloadData()

})

let cancelAction = UIAlertAction(title: "Cancel",

style: .default) { (action: UIAlertAction) -> Void in

}

alert.addTextField {

(textField: UITextField) -> Void in

}

alert.addAction(saveAction)

alert.addAction(cancelAction)

present(alert,

animated: true,

completion: nil)

}


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "showFeatures" {

if let indexPath = self.tableView.indexPathForSelectedRow {

let object = self.fetchedResultsController.object(at: indexPath)

let controller = (segue.destination as! UINavigationController).topViewController as! MasterViewController

controller.projectItem = object

controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem

controller.navigationItem.leftItemsSupplementBackButton = true

/

/

/

/

/

}

}

}

/

override func numberOfSections(in tableView: UITableView) -> Int {

return self.fetchedResultsController.sections?.count ?? 0

}


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

let sectionInfo = self.fetchedResultsController.sections![section]

print("Number of object \(sectionInfo.numberOfObjects)")

return sectionInfo.numberOfObjects

}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? ProjectTableViewCell

let event = self.fetchedResultsController.object(at: indexPath)

print("New Cell \(event.projectName) and date \(event.projectDate) and feature count \(event.projectRelationship?.count)")

cell?.projectTitle.text = event.projectName

let formatter = DateFormatter();

formatter.dateFormat = "yyyy-MM-dd";

cell?.projectDate.text = formatter.string(from: event.projectDate as! Date)

return cell!

}


override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

/

return true

}


override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

if editingStyle == .delete {

let context = self.fetchedResultsController.managedObjectContext

context.delete(self.fetchedResultsController.object(at: indexPath))

do {

try context.save()

} catch {

/

/

let nserror = error as NSError

fatalError("Unresolved error \(nserror), \(nserror.userInfo)")

}

self._fetchedResultsController = nil

self.tableView.reloadData()

}

}


func configureCell(_ cell: UITableViewCell, withEvent event: SSProjectMO) {

cell.textLabel!.text = event.projectName!.description

/

}




var fetchedResultsController: NSFetchedResultsController<SSProjectMO> {

if _fetchedResultsController != nil {

return _fetchedResultsController!

}

let fetchRequest: NSFetchRequest<SSProjectMO> = SSProjectMO.fetchRequest()

/

fetchRequest.fetchBatchSize = 20

/

let sortDescriptor = NSSortDescriptor(key: "projectName", ascending: false)

fetchRequest.sortDescriptors = [sortDescriptor]

/

/

let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName:nil)

aFetchedResultsController.delegate = self

_fetchedResultsController = aFetchedResultsController

do {

try _fetchedResultsController!.performFetch()

} catch {

/

/

let nserror = error as NSError

fatalError("Unresolved error \(nserror), \(nserror.userInfo)")

}

return _fetchedResultsController!

}

var _fetchedResultsController: NSFetchedResultsController<SSProjectMO>? = nil



}

Accepted Reply

The problem is the part where you use this line:

let newProject = SSProjectMO(context: context)

That doesn't create a new entity in the database. That just creates a new instance of the managed object half of the object, an API that exists because the runty me runtime (darned auto-correct 😝) needs to do that sort of thing when it loads data from the persistent store.


If you're trying to make a new entity instance, use code like the line you used for the Feature:

newItem = NSEntityDescription.insertNewObject(forEntityName: "Feature", into: context) as! SSFeatureMO


Or use a fetch request to find an existing entity.


Don't use the context: constructor.


Edit: Fixed typos in the reply.

Replies

The problem is the part where you use this line:

let newProject = SSProjectMO(context: context)

That doesn't create a new entity in the database. That just creates a new instance of the managed object half of the object, an API that exists because the runty me runtime (darned auto-correct 😝) needs to do that sort of thing when it loads data from the persistent store.


If you're trying to make a new entity instance, use code like the line you used for the Feature:

newItem = NSEntityDescription.insertNewObject(forEntityName: "Feature", into: context) as! SSFeatureMO


Or use a fetch request to find an existing entity.


Don't use the context: constructor.


Edit: Fixed typos in the reply.