I have a UITableView in a UIViewController that consists of dynamic multiple sections and dynamic multiple cells/rows in each section.
I have tried using actions, closures, index path. Etc
What I want to do is tap a UIButton in a custom UITableViewCell and have the row and corresponding data be deleted/removed.
Currently when I try this I can delete the first cell in a section but then when I try to delete the last one in the section I get a crash, Index out of bounds. It seems the UIButton retains the old indexPath.row
I have tried to reloadData() for the table but I haven’t been able to successfully solve this.
Using this solution from SO (UIButton action in table view cell):
import UIKit
class ViewController: UIViewController {
    @IBOutlet weak var myTableView: UITableView!
    var sections: [String] = []
    var items: [[String]] = []
    var things: [[String]] = []
    var things2: [[String]] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        things = [["A","B","C","D"],["X","Y","Z"],["J","A","A","E","K"]]
        things2 = [["Q","W","E","R"], ["G","H","J"]]
        items.append(contentsOf: things)
        items.append(contentsOf: things2)
        let secs: Int = Int.random(in: 1...items.count)
        for num in 1...secs {
            if num == 1 {
                sections.append("My Stuff")
            } else {
                sections.append("Section \(num)")
            }
        }
    }
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items[section].count
    }
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sections[section]
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCustomTableViewCell
        cell.theLabel?.text = items[indexPath.section][indexPath.row]
        if indexPath.section == 0 {
            cell.deleteButton.isHidden = false
            cell.editButton.isHidden = false
            cell.deleteButton.addAction {
                // This is broken, when the action is added in the closure. It persists the indexPath as it is, not changed even with a reloadData()
                self.items[indexPath.section].remove(at: indexPath.row)
                self.myTableView.deleteRows(at: [indexPath], with: .fade)
            }
            cell.editButton.addAction {
                print("I'm editing this row")
                cell.backgroundColor = UIColor.magenta
            }
        } else {
            cell.deleteButton.isHidden = true
            cell.editButton.isHidden = true
        }
        return cell
    }
}
// used from https://stackoverflow.com/questions/28894765/uibutton-action-in-table-view-cell/41374087
extension UIControl {
    func addAction(for controlEvents: UIControl.Event = .primaryActionTriggered, action: @escaping () -> ()) {
        let sleeve = ClosureSleeve(attachTo: self, closure: action)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
    }
}
class ClosureSleeve {
    let closure: () -> ()
    init(attachTo: AnyObject, closure: @escaping () -> ()) {
        self.closure = closure
        objc_setAssociatedObject(attachTo, "[\(arc4random())]", self,.OBJC_ASSOCIATION_RETAIN)
    }
    @objc func invoke() {
        closure()
    }
}
The problem with this being used in multiple sections is that it retains the original indexPath and row.
Example: if section 0 has two items (0,1) I delete section 0 item 0 - it works as expected. Then when I tap the button to delete the remaining cell, which was section 0 item 1 and should have been reloaded to section 0 item 0. It fails because the closure still sees the code block as being section 0 item 1. Even through a reloadData() the closure still sees the original assignment of indexPath.section and indexPath.row
Anyone have any ideas?
I'm thinking it has something to do with the retain, but honestly, I'm drawing a blank. I've looked at this code in a debugger for a day or two and I'm frustrated and need some Yoda help...