I’m trying to make a simple note app using Core Data, but I’m running into problem with fetchedResultsController.object(at: indexPath) & passing the data onto my View Controller. using prepare(for segue: UIStoryboardSegue, sender: Any?).
My Core Data Entity is named Notes with the following Attributes
- dateStamp Integer 64
- noteName String
- noteImage Data
- noteDescription String
However in order to understand the problem I’ve made separate project & limited it to just the noteName & dateStamp.
So there are three Controllers & one Helper file
- MasterViewController
- AddViewController
- DetailViewController
- EditViewController
- dateHelper
The MasterView is my UITableController with my NSFetchedResultsControllerDelegate, so the code is as follows….
import UIKit
import CoreData
class MasterViewController: UITableViewController {
private let noteCreationTimeStamp : Int64 = Date().timetoSeconds()
var managedObjectContext: NSManagedObjectContext? {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
}
var fetchedResultsController: NSFetchedResultsController<Notes>!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
fetchFromCoreData()
navigationItem.leftBarButtonItem = editButtonItem
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
navigationItem.rightBarButtonItem = addButton
}
@objc
func insertNewObject(_ sender: Any) {
let addController = storyboard?.instantiateViewController(withIdentifier: "addNotes")
as! UINavigationController
self.present(addController, animated: true, completion: nil)
}
private func configureCells(cell: noterViewCell, withEvent note: Notes, indexPath: IndexPath) {
let record = fetchedResultsController.object(at: indexPath)
cell.noteName.text = record.noteName
cell.dateLabel.text = dateHelper.convertDate(date: Date.init(seconds: record.dateStamp))
if let noteName = record.value(forKey: "noteName") as? String, let dateTime = record.value(forKey: "dateStamp") as? Int64 {
cell.noteName.text = noteName
cell.dateLabel.text = dateHelper.convertDate(date: Date.init(seconds: dateTime))
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = self.fetchedResultsController?.sections else {
fatalError("No sections in fetchedResultsController")
}
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "noteCell", for: indexPath) as! noterViewCell
guard let object = self.fetchedResultsController?.object(at: indexPath) else {
fatalError("Attempt to configure cell without a managed object")
}
configureCells(cell: cell, withEvent: object, indexPath: indexPath)
cell.selectbutton.addTarget(self, action: #selector(selectNote(_:)), for: .touchUpInside)
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let context = fetchedResultsController.managedObjectContext
context.delete(fetchedResultsController.object(at: indexPath))
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error (nserror), (nserror.userInfo)")
}
}
}
func fetchFromCoreData() {
let fetchRequest: NSFetchRequest<Notes> = Notes.fetchRequest()
fetchRequest.fetchBatchSize = 800
let creationDateSortDescriptor = NSSortDescriptor(key: "dateStamp", ascending: false)
//let nameSortDescriptor = NSSortDescriptor(key: "noteName", ascending: false)
fetchRequest.sortDescriptors = [creationDateSortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: "dateStamp", cacheName: nil)
aFetchedResultsController.delegate = self
fetchedResultsController = aFetchedResultsController
do {
try fetchedResultsController.performFetch()
self.tableView.reloadData()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error (nserror), (nserror.localizedDescription), (nserror.localizedFailureReason ?? "could not retrieve")")
//print("Could not save note to CoreData: (error.localizedDescription)")
}
}
}
extension MasterViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
default:
return
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
tableView.reloadRows(at: [newIndexPath!], with: .fade)
if let updateIndexPath = newIndexPath {
let cell = self.tableView.cellForRow(at: updateIndexPath) as! noterViewCell
let event = anObject as! Notes
cell.dateLabel.text = event.noteName
cell.dateLabel.text = dateHelper.convertDate(date: Date.init(seconds: event.dateStamp))
}
case .move:
configureCells(cell: tableView.cellForRow(at: indexPath!) as! noterViewCell, withEvent: anObject as! Notes, indexPath: indexPath!)
tableView.moveRow(at: indexPath!, to: newIndexPath!)
@unknown default:
fatalError("Unresolved error")
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
}
My AddViewController
import UIKit
import CoreData
class AddViewController: UIViewController {
@IBOutlet var noteField: UITextField!
@IBOutlet var dateStamp: UILabel!
private let noteCreationTimeStamp : Int64 = Date().timetoSeconds()
let masterView = MasterViewController()
var isExisting: Bool = false
var note:Notes? = nil
var managedObjectContext: NSManagedObjectContext? {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}
var detailItem: Notes? {
didSet {
// Update the view.
configureView()
}
}
func configureView() {
if let detail = detailItem {
if let noteFieldLabel = noteField, let dateTime = dateStamp {
noteFieldLabel.text = detail.noteName
dateTime.text = dateHelper.convertDate(date: Date.init(seconds: detail.dateStamp))
}
}
}
@IBAction func saveImageButtonPressed(_ sender: Any) {
let context = self.managedObjectContext
let newEvent = Notes(context: context!)
newEvent.noteName = noteField.text
newEvent.dateStamp = noteCreationTimeStamp
do {
try context?.save()
} catch {
let error = error as NSError
fatalError("Unresolved error (error), (error.localizedDescription)")
}
let isPresentingMode = self.presentingViewController is UINavigationController
if isPresentingMode {
self.dismiss(animated: true, completion: nil)
}
else {
self.navigationController!.pushViewController(masterView, animated: true)
}
}
@IBAction func cancelView(_ sender: AnyObject) {
let isPresentingMode = self.presentingViewController is UINavigationController
if isPresentingMode {
self.dismiss(animated: true, completion: nil)
}
}
}
Everything is good there my saveImageButtonPressed saves the data to my managedObjectContext and displays onto the UITableview with no problems.
This code for my DetailViewController
import UIKit
import CoreData
class DetailViewController: UIViewController {
@IBOutlet var dateLabel: UILabel!
@IBOutlet var noteField: UILabel!
let masterView = MasterViewController()
var notes: Notes?
var myNotes = [Notes]()
var isExisting: Bool = false
var index = IndexPath()
private let noteCreationTimeStamp : Int64 = Date().timetoSeconds()
var managedObjectContext: NSManagedObjectContext? {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
}
var detailItem: Notes? {
didSet {
// Update the view.
configureView()
}
}
func configureView() {
if let detail = detailItem {
if let noteFieldLabel = noteField, let dateStamp = dateLabel {
noteFieldLabel.text = detail.noteName
dateSt