Zack Shapiro Zack Shapiro - 2 months ago 23
Swift Question

Deleting a table view row with Swift 3 and CoreData

I'm trying to implement swipe to delete a table view cell in my app and I'm getting a crash:


'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'


I have my ViewController, and in the ViewControllerDataSource file, I'm conforming to
UITableViewDataSource


func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let managedObjectContext = appDelegate.managedObjectContext, editingStyle == .delete else { return }

let request: NSFetchRequest<Meditation> = NSFetchRequest(entityName: "Meditation")
let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
request.sortDescriptors = [sortDescriptor]

if let sessions = try? managedObjectContext.fetch(request) as [Meditation] {
managedObjectContext.delete(sessions[indexPath.row])

tableView.deleteRows(at: [indexPath], with: .fade)
fetchEntries {
tableView.reloadData()
}
}
}


This is what
fetchEntries
looks like

func fetchEntries(completion: (() -> Void)? = nil) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let managedObjectContext = appDelegate.managedObjectContext
else { return }

var arrayOfEntries: [Entry] = []
let request: NSFetchRequest<Meditation> = NSFetchRequest(entityName: "Meditation")
let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
request.sortDescriptors = [sortDescriptor]

if let objects = try? managedObjectContext.fetch(request) as [Meditation] {
meditationSessions = objects

for (index, object) in objects.enumerated() {
// set some values that correspond to visual elements in the cell

let timeLabel = lengthMinutesLabel(minutesString: lengthMinutes(object.length))

let entry = Entry(
sessionLength: lengthMinutes(object.length),
sessionTimeUnit: timeLabel,
sessionStartTime: "\(object.date.format())",
sessionJournalEntry: object.journalEntry,
sessionStreakTop: showStreak.top,
sessionStreakBottom: showStreak.bottom
)

arrayOfEntries.append(entry)
self.arrayOfEntries = arrayOfEntries
}
}

completion?()
}


I'm not quite sure what I'm doing wrong here that's causing this crash. Unfortunately there's no completion block for the CoreData delete functionality if the crash is caused by a race condition.

I've tried a variety of different orders of operations for the last 7-9 lines of the
tableView:editingStyle:
function to no avail. Any suggestions would be helpful. Thanks!

Answer Source

When you delete data in coreData it wont be saved by coreData unless you save the action. The delete action should be like this..

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
  if editingStyle == .delete {
    managedObjectContext.delete(sessions[indexPath.row])
    do {
      try managedObjectContext.save()
      tableView.reloadData()
    } catch let error as NSError {
      print("Could not save. \(error), \(error.userInfo)")
    }
  }
}