RUSTAM
RUSTAM

Reputation: 13

Get trouble of updating tableview rows when deleting Firebase data

something goes wrong when trying to update rows of tableview after delete of Firebase data.

Below is method I use.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in

        let cell = self.messages[indexPath.row]
        let b = cell.msgNo

        let action = MyGlobalVariables.refMessages.child(MyGlobalVariables.uidUser!)
        action.queryOrdered(byChild: "msgNo").queryEqual(toValue: b).observe(.childAdded, with: { snapshot in

            if snapshot.exists() { let a = snapshot.value as? [String: AnyObject]

                let autoId = a?["autoID"]

                action.child(autoId as! String).removeValue()

                self.messages.remove(at: indexPath.row)

                tableView.deleteRows(at: [indexPath], with: .automatic)
            } else {
                print("snapshot empty")
            }}) }
   ...
   return [delete, edit, preview]
}

Initially I checked whole logic without including line /*action.child(autoId as! String).removeValue()*/ then it works normally and removes rows as should be. But once I add this line it removes data from Firebase but tableview is updated in strange way by adding new rows below existing

Attached snapshot of my application with/without this line of code

Upvotes: 0

Views: 81

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598728

My guess is that somewhere else in your application you have code like action .observe(.value, which shows the data in the table view. When you delete a node from the database, the code that populates the database gets triggered again, and it adds the same data (minus the node that you removed) to the table view again.

When working with Firebase it's best to follow the command query responsibility segregation principle, meaning that you keep the code that modifies the data completely separate from the flow that displays the data. That means that your code that deletes the data, should not try to update the table view. So something more like:

let action = MyGlobalVariables.refMessages.child(MyGlobalVariables.uidUser!)
action.queryOrdered(byChild: "msgNo").queryEqual(toValue: b).observe(.childAdded, with: { snapshot in
    if snapshot.exists() { let a = snapshot.value as? [String: AnyObject]
        let autoId = a?["autoID"]
        action.child(autoId as! String).removeValue()
    } else {
        print("snapshot empty")
    }}) }

All the above does is remove the selected message from the database.

Now you can focus on your observer, and ensuring it only shows the messages once. There are two options for this:

  1. Always clear self.messages when your .value completion handler gets called before you add the messages from the database. This is by far the simplest method, but may cause some flicker if you're showing a lot of data.
  2. Listen to the more granular messages like .childAdded and .childRemoved and update self.messages based on those. This is more work in your code, but will result in a smoother UI when there are many messages.

Upvotes: 1

Related Questions