Search code examples
iosswiftuitableviewnsobjectunrecognized-selector

How to manage #selector with separate UITableViewDataSource class?


I have a separate class for UITableViewDataSource for my UITableView

import UIKit

class CardDataSource: NSObject, UITableViewDataSource {

    //MARK:- Property
    var medialObjects: [MediaCardObject]?
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    var viewController: AddNewVC?

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return medialObjects?.count ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let obj = medialObjects![indexPath.row]

        let cell = tableView.dequeueReusableCell(withIdentifier: "MediaTextTableViewCell") as! MediaTextTableViewCell

        cell.btnDelete.tag = indexPath.row
        cell.btnDelete.addTarget(self, action: #selector(AddNewVC.deleteItem(sender:)), for: .touchUpInside)
        cell.tfCardText.text = obj.path
        cell.tfCardText.tag = indexPath.row
        cell.tfCardText.returnKeyType = .done
        cell.tfCardText.delegate = viewController
        cell.selectionStyle = .none
        return cell
    }
}

Data is loading fine with this on my UITableView but the problem is when I click on delete button it's crashing and says

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MediaCards.CardDataSource deleteItemWithSender:]: unrecognized selector sent to instance 0x600003394cc0'

I have also tried

cell.btnDelete.addTarget(self, action: #selector(viewController?.deleteItem(sender:)), for: .touchUpInside)

Here is the delete method in AddNewVC

//MARK:- Delete
@objc func deleteItem(sender: UIButton) {

    let selectedIndex = sender.tag
    deleteObjectIndexPath = IndexPath(row: selectedIndex, section: 0)
    confirmDelete()
}

and in AddNewVC class

var tblDataSource = CardDataSource()

override func viewDidLoad() {
    super.viewDidLoad()

    tblDataSource.viewController = self
    tblMediaCards.dataSource = tblDataSource

    if forEdit {
        let cards = mediaObject?.cards?.allObjects as! [MediaCard]
        for obj in cards {
            let mediaCardObj = MediaCardObject.init(type: obj.type ?? "", path: obj.path ?? "", index: Int(obj.index))
            self.medialObjects.append(mediaCardObj)
        }

        medialObjects = medialObjects.sorted(by: { $0.index! < $1.index! })
        tblDataSource.medialObjects = medialObjects

        self.tblMediaCards.reloadData()
    }
}

But it's still crashing with same reason. Although delegate is working fine.


Solution

  • The target is wrong:

    cell.btnDelete.addTarget(self, action: #selector(AddNewVC.deleteItem(sender:)), for: .touchUpInside)
    

    The method AddNewVC.deleteItem(sender:) is not part of self rather of a different class member. Try with:

    cell.btnDelete.addTarget(self.viewController, action: #selector(AddNewVC.deleteItem(sender:)), for: .touchUpInside)
    

    I would rather have a target method in the same viewcontroller and then having this call the datasource/subviewcontroller method, just for safety.