1

I have a delete function for my table which is a delegate of a fetch controller. When i swipe delete a row it clears the object and deletes its data, but the cell persists as an empty row.

My fetch and table code looks like this, any idea why it doesnt delete the row too?:

 func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
            switch (type) {
            case .update:
                if let indexPath = indexPath, let cell = workoutDesignerTable.cellForRow(at: indexPath) as? RoutineTableViewCell {
                    configure(cell, at: indexPath)
                }
                break;
            default:
                print("...")
            }
        }
    }

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

        let delete = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in
            let UserExercise = self.fetchedResultsController.managedObjectContext
            UserExercise.delete(self.fetchedResultsController.object(at: indexPath))
            do {
                try UserExercise.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
            self.workoutDesignerTable.reloadData()
        }

        let edit = UITableViewRowAction(style: .normal, title: "Edit") { (action, indexPath) in
            let cell = tableView.cellForRow(at: indexPath)
            self.updateExercise = self.fetchedResultsController.object(at: indexPath)
            self.performSegue(withIdentifier: self.segueEditUserExerciseViewController, sender: cell)
        }
            edit.backgroundColor = UIColor.lightGray
            return [delete, edit]
    }

Pics of the issue below:

enter image description here

enter image description here

enter image description here

I have added the following func which hasn't resolved the issue

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let UserExercise = fetchedResultsController.managedObjectContext
        UserExercise.delete(fetchedResultsController.object(at: indexPath))
        do {
            try UserExercise.save()
        } catch {
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
    tableView.deleteRows(at: [indexPath], with: .fade)
}

also here is the numberOfRowsInSection as requested

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let userExercises = fetchedResultsController.fetchedObjects else { return 0 }
    return userExercises.count
}

enter image description here

I have added the above console print to show after deleting the print of the object which seems to read as having a fault if this helps narrow it down

added the fetchresultscontroller code below

fileprivate lazy var fetchedResultsController: NSFetchedResultsController<UserExercise> = {

    let fetchRequest: NSFetchRequest<UserExercise> = UserExercise.fetchRequest()
    if self.userRoutine == nil {
        fetchRequest.predicate = NSPredicate(format: "usersroutine == nil")
    } else if self.userRoutine != nil {
        fetchRequest.predicate = NSPredicate(format: "usersroutine.name == %@", self.userRoutine!)
    }
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "dateCreated", ascending: true)]

    //controller config
    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)

    fetchedResultsController.delegate = self
    return fetchedResultsController
}()

Additional Controller Delegate Code:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        workoutDesignerTable.beginUpdates()
    }

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        workoutDesignerTable.endUpdates()
        updateView()
    }

    // MARK: - ADDING TABLE ROW AND EDITORIAL FEATURES

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
        switch (type) {
        case .update:
            if let indexPath = indexPath, let cell = workoutDesignerTable.cellForRow(at: indexPath) as? RoutineTableViewCell {
                configure(cell, at: indexPath)
            }
            break;
        default:
            print("...")
        }
    }
}
jwarris91
  • 902
  • 9
  • 24
  • 1
    What does your `numberOfItemsForRowAtIndexPath` method look like? That's the method that tells your tableview how many rows to show. – Mark Jan 26 '17 at 14:49
  • I dont have that specific method jsut numberOfRowsInSection and CellForTowAt – jwarris91 Jan 26 '17 at 14:52
  • Haha sorry that's the method I was referring to (numberOfRowsInSection). – Mark Jan 26 '17 at 14:58
  • adding it to the OP now for you 1 sec – jwarris91 Jan 26 '17 at 14:58
  • I just ran a print command in that function, even after deleting its showing the exercise count as 1 hence the row, so again something must be up with the delete function where it isnt clearing the row or count isnt updating which is most likely – jwarris91 Jan 26 '17 at 15:02

2 Answers2

3

You need to do 3 steps in order to achieve what you want. Detect Swipe, Remove From Model, Update View. Here is an Example:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { // Detect Swipe
    if editingStyle == .delete {
        restaurantNames.remove(at: indexPath.row) // Remove from Model
    }
    tableView.deleteRows(at: [indexPath], with: .fade) // Update view
}
Mago Nicolas Palacios
  • 2,501
  • 1
  • 15
  • 27
  • what would resturantNames translate to in my code? Im pretty sure this wont clear anything from core data just the table? – jwarris91 Jan 26 '17 at 14:54
  • 1
    I think you are using Core Data. In my Case, I Remove it from my array of Restaurant Names. In Your case, That line of code will be removing you data from core data. – Mago Nicolas Palacios Jan 26 '17 at 14:56
  • I have updated the OP under the images with the new code I added, it hasnt resolved the issue, same problem – jwarris91 Jan 26 '17 at 14:58
  • 1
    I Think your problem is on the amount of rows you are showing on your table, Can you share the code that tells the Table view how many rows it has? – Mago Nicolas Palacios Jan 26 '17 at 15:00
  • yup ive added that to the OP as requested by the other responder – jwarris91 Jan 26 '17 at 15:00
  • We might try some debugging, Print userExcerice count When you finish deleting it with my solutions. Also try printing some print("Success") when you try and save. – Mago Nicolas Palacios Jan 26 '17 at 15:03
  • I actually did just try this and posted in the comments on the OP, it returns 1 even after deleting the exercise which would cause the issue, for some reason deleting the object isnt clearing it form the count even though it is clearing it from the coredata – jwarris91 Jan 26 '17 at 15:05
  • Your problem then is deleting, not updating the Table. Try searching about deleting in core data, like this: http://stackoverflow.com/questions/26047013/delete-data-from-coredata-swift – Mago Nicolas Palacios Jan 26 '17 at 15:07
2

First, add this function (which Mago mentionned).

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let UserExercise = fetchedResultsController.managedObjectContext
        UserExercise.delete(fetchedResultsController.object(at: indexPath))
        do {
            try UserExercise.save()
        } catch {
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}

Notice we don't reload the tableview here. The tableview will automatically remove the cell for you, so you just need to worry about updating your data source.

There's a great tutorial on using NSFetchedResultsControllers here for more info on how you can use them. Also, here's the official documentation.

EDIT Here's the implementation of the NSFetchedResultsControllerDelegate methods you need to implement, in Swift:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.beginUpdates()
}

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: .automatic)
    case .delete:
        tableView.deleteRows(at: [indexPath!], with: .automatic)
    case .update:
        tableView.reloadRows(at: [indexPath!], with: .automatic)
    case .move:
        tableView.moveRow(at: indexPath!, to: newIndexPath!)
    }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
}

You're basically mapping the changes produced by the fetched results controller to the tableview.

Mark
  • 7,167
  • 4
  • 44
  • 68
  • If that doesn't work, you may have to set the `NSFetchedResultsController` delegate property to your VC, and handle the appropriate callbacks (this is covered in the first link I posted). – Mark Jan 26 '17 at 17:33
  • Hi Mark adding this function doesn't fix the issue unfortunately its exactly the same. I also believe i do set the table as delegate and datasource already – jwarris91 Jan 26 '17 at 17:50
  • Are you referring to uitabelviewdelegate and uitableviewdatasource? I'm talking about NSFetchedResultsControllerDelegate. – Mark Jan 26 '17 at 17:51
  • yes i was, so do i just add NSFetchedResultsControllerDelegate = self to the viewdidload? – jwarris91 Jan 26 '17 at 17:52
  • You need to set the property on the instance, so `fetchedResultsController.delegate = self`. Take a look at this link, it walks you through every step: https://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller – Mark Jan 26 '17 at 17:53
  • ill give the article a read but adding that to the viewdidload also didnt resolve the issue – jwarris91 Jan 26 '17 at 17:55
  • Yeah, you also need to implement the correct delegate methods. The article covers that as well. – Mark Jan 26 '17 at 17:56
  • is that article in C? im new to programming and only know swift so its a little tricky to read the code! – jwarris91 Jan 26 '17 at 17:58
  • I can add the Swift version to my answer in a few hours. – Mark Jan 26 '17 at 18:02
  • thanks that would be great then if it resolves it i can vote it for others to reference, otherwise ill give the articles a read and do some googling in the mean time to better understand an issue – jwarris91 Jan 26 '17 at 18:07