Creating custom UITableViewCell styles with dequeueReusableCell(withIdentifier:for:)

The trick with Swift always seems getting the right combination of object calls, the right naming, and wading through error messages. I have spent so much time on StackOverflow searching for “custom uitableviewcell” or “custom uitableviewcontroller” and “dequeuereusablecell indexPath fails”, and the Apple docs are as wordy as ever. Trying to use the dequeueReusableCell with the for:indexPath kept crashing my app with error messages like:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier CustomCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

After much frustration, this is the solution that seems to work the best:

Create a New Table Cell Class

Go ahead and create a new class with a XIB/NIB that inherits from UITableViewCell. You can do this programmatically, but sometimes visually working with autolayout constraints is just easier to grok. Make sure to bind all the labels and images and other UIView things from the NIB to the .swift class.

Create a New Table Controller

Create a new class inheriting from UITableViewController but no need to create a NIB unless you need. In the cellForRowAt method, you’ll want to dequeue as your custom table cell.

    let REUSE_IDENTIFIER = "YourCustomID"

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: REUSE_IDENTIFIER, for: indexPath) as! TableViewCell

        // Configure the cell...

        return cell
    }

Register the cell NIB on the tableView

Finally, the part that just killed me for the two days was how/when/where to register the NIB. And, the easiest is in the viewDidLoad of the table view controller:

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UINib.init(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: REUSE_IDENTIFIER)
    }

Notice that the register is on the tableView.

And that, is how you do it.