Home > Back-end >  Error deleting TableViewCell from TableView that uses CoreData
Error deleting TableViewCell from TableView that uses CoreData

Time:05-11

I have tried any and all current StackOverflow suggestions I could find and none are seeming to work so I must resort to asking myself.

I am attempting to delete a journal entry from a tableView that is populated with custom cells. (All journal entries are created on user inputs and then stored in CoreData and the UITableView is populated with these entries from CoreData).

On deleting the table view cell, the entry is being successfully deleted but the app crashes with the error

Terminating app due to uncaught exception 'NSInternalInconsistencyException'

If I reload the app and go to the page with this tableView, the entry is no longer there in the TableView so it is being deleted from CoreData, just not from the table. Any help on this would be incredibly appreciated!

The function throwing the error is as follows:

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
print("table view index cell  \(tableView.indexPathsForVisibleRows!)")
if editingStyle == .delete {
    print("Deleting Journal Entry from Core Data")
    let managedContext = appDelegate.persistentContainer.viewContext
    managedContext.delete(fetchedResultsController.object(at: indexPath))
    do {
        try managedContext.save()
    } catch {
        print("error saving after deleting")
    }
    // THIS LINE THROWS THE ERROR :(
    tableView.deleteRows(at: [indexPath], with: .fade)
}
}

Entire ViewController Class Code:

import UIKit
import CoreData
import Foundation

class JournalListViewController: UIViewController, UITableViewDelegate, 
UITableViewDataSource, NSFetchedResultsControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate{

@IBOutlet weak var journalTableView: UITableView!
@IBOutlet weak var noEntriesLabel: UILabel!

let parks = NationalPark.getAllParks()
let dateFormatter = DateFormatter()
var journalIndex = Int()
var journalImage = UIImage()
let appDelegate = UIApplication.shared.delegate as! AppDelegate


// Core data set up
lazy var fetchedResultsController: NSFetchedResultsController<Entry> = {
  
    let managedContext = appDelegate.persistentContainer.viewContext
    let fetchRequest: NSFetchRequest<Entry> = Entry.fetchRequest()
    fetchRequest.sortDescriptors = [NSSortDescriptor (key: "park", ascending: true)]
    
    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)
    
    fetchedResultsController.delegate = self
    return fetchedResultsController
    
} ()



override func viewDidLoad() {
    super.viewDidLoad()
    
    self.journalTableView.delegate = self
    self.journalTableView.dataSource = self
    
    
    // Needed to Fetch Core Data
    do {
        try fetchedResultsController.performFetch()
        print ("fetched")
    } catch {
        let fetchError = error as NSError
        print("\(fetchError) , \(fetchError.localizedDescription)")
    }
}



// MARK: - Navigation

// Prepare segue to New Entry Controller
// Pass the index of list items, connected to image saving
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "toNewEntry" {
        let destinationVC = segue.destination as! NewJournalEntryViewController
        destinationVC.index = journalIndex;
    }
    else if segue.identifier == "toJournalEntryData" {
        let destinationVC = segue.destination as! JournalDataViewController
        let selectedJournalEntry: Entry = fetchedResultsController.object(at: journalTableView.indexPathForSelectedRow!)
        
        // GET IMAGE FOR SELECTED JOURNAL CELL FROM SAVED DOCUMENTS
        // then set image to UIImage for Data View
        let imageURL = getDocumentsDirectory().appendingPathComponent("image\(selectedJournalEntry.entryNum).jpg")
        let image = UIImage(contentsOfFile: imageURL.path)
        
        // print("\(journalImage)")
        
        destinationVC.journalImage = image!
        // print("THE RETURN PATH IS ****** \(imageURL)")
        
        
        // STORE INFORMATION INTO DESTINATION VIEW VARIABLES
        destinationVC.title = "Journal Entry"
        // destinationVC.journalEntryNum = selectedJournalEntry.entryNum
        destinationVC.journalTitle = selectedJournalEntry.park!
        destinationVC.journalDate = String(journalIndex)
        destinationVC.journalReport = selectedJournalEntry.report!
    }
}

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
}


// MARK: - Table View Methods

// Defines # Of Rows In TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let journalEntries = fetchedResultsController.fetchedObjects
    else {
        return 0
    }
    print("Journal Entries \(journalEntries.count)")
    if (journalEntries.count == 0) {
        noEntriesLabel.text = "You have no journal entries :("
        noEntriesLabel.isHidden = false
    } else { noEntriesLabel.isHidden = true
        journalIndex = journalEntries.count
    }
    return journalEntries.count
}


// Configure each table row cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    // Core Data Cell Getter
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "JournalTableCell", for: indexPath) as? JournalListTableViewCell else {
        fatalError("expected Index path")
    }
    
    // Get the journalEntry number from core data to correspond with each cell
    let journalEntry = fetchedResultsController.object(at: indexPath)
    
    // SAME CODE FROM ABOVE IN THE SEGUE!
    // Gets Image from documents and sets it to the Image of the cell
    let imageURL = getDocumentsDirectory().appendingPathComponent("image\(journalEntry.entryNum).jpg")
    let image = UIImage(contentsOfFile: imageURL.path)
    
    // print("IMAGE AT CELL \(journalEntry): \(imageURL.path)")
    cell.journalImageView.image = image!
    // END SAME CODE
    
    // var dateInStringFormat = (dateFormatter.string(from: journalEntry.date!))
    cell.journalTitleLabel.text = journalEntry.park
    cell.journalDateLabel.text = String(journalEntry.entryNum)
    // cell.journalDateLabel.text = dateInStringFormat
    

    // configure cell styling
    cell.configure()
    return cell

}

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
    return .delete
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    print("table view index cell  \(tableView.indexPathsForVisibleRows!)")
    if editingStyle == .delete {
        print("Deleting Journal Entry from Core Data")
        let managedContext = appDelegate.persistentContainer.viewContext
        managedContext.delete(fetchedResultsController.object(at: indexPath))
        do {
            try managedContext.save()
        } catch {
            print("error saving after deleting")
        }
        tableView.deleteRows(at: [indexPath], with: .fade)
    }
}


// Anytime the View Appears, refresh for new data
override func viewDidAppear(_ animated: Bool) {
    // Needed to Fetch Core Data
    do {
        try fetchedResultsController.performFetch()
        print ("fetched")
    } catch {
        let fetchError = error as NSError
        print("\(fetchError) , \(fetchError.localizedDescription)")
    }
    
    getAllItems()
}


// Simply reloads table view
func getAllItems() {
    journalTableView.reloadData()
}

}

CodePudding user response:

When using an NSFetchedResultsController (FRC) you don't need to and should not delete the table rows yourself.

Just implement the FRC delegate method controller(_:didChange:at:for:newIndexPath:).

In this method then do something like (depending on your sections):

    let range = NSMakeRange(0, tableView.numberOfSections)
    let sections = NSIndexSet(indexesIn: range)
    tableView.reloadSections(sections as IndexSet, with: .automatic)

So delete tableView.deleteRows(at: [indexPath], with: .fade) and implement the above.

This should fix your crash on that line, but I didn't check if other things are wrong.

Good luck!

  • Related