I recently asked a question on how to implement a delete function for my CoreData entities. (here:How do I delete CoreData entries in a tableview that uses a persistentcontainer?) I was informed that I should make use of the NSFetchResultsController so I began to work on that. I thought I got it right my code now crashes immediately after running.
I have tried using a tutorial to implement the FetchedResultsController. I am pretty sure I have followed all the steps correctly to implement into my app but it still crashes. My attempt is provided below. My original code which works is in the paragraph above.
ViewController.swift
import UIKit
import CoreData
class ViewController: UITableViewController {
//var alarmItems: [NSManagedObject] = []
let cellId = "cellId"
private let persistentContainer = NSPersistentContainer(name: "AlarmItems")
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItems> = {
//create fetch request
let fetchRequest: NSFetchRequest<AlarmItems> = AlarmItems.fetchRequest()
//configure fetch request
//fetchRequest.sortDescriptors = [NSSortDescriptor(key: )]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self as? NSFetchedResultsControllerDelegate
return fetchedResultsController
}()
override func viewDidLoad() {
super.viewDidLoad()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
//let managedContext = appDelegate.persistentContainer.viewContext
//let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "AlarmItems")
do {
//alarmItems = try managedContext.fetch(fetchRequest)
try fetchedResultsController.performFetch()
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
@objc func addAlarmItem(_ sender: AnyObject) {
//print("this works")
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItems", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
//NSFetchedResultsController.append(item)
tableView.reloadData()
} catch let err as NSError {
print("Failed to save an item", err)
}
}
@objc func exportCSV(_ sender: AnyObject) {
//will work on exporting csv in the future
return
}
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
//return alarmItems.count
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let alarmItem = fetchedResultsController.object(at: indexPath)
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
}
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An instance of NSFetchedResultsController requires a non-nil fetchRequest and managedObjectContext'
They also say that it failed to load the model named AlarmItems even though I already have the fetch request function in my code.
First of all delete let persistentContainer = NSPersistentContainer(name: "AlarmItems")
and get the context from AppDelegate
The error is incomplete
An instance of NSFetchedResultsController requires a non-nil fetchRequest with sort descriptors and managedObjectContext
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItems> = {
//create fetch request
let fetchRequest: NSFetchRequest<AlarmItems> = AlarmItems.fetchRequest()
//configure fetch request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alarmAttributes", ascending: true)]
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()
And as already mentioned please name the NSManagedObject
subclass in singular form AlarmItem