UITableView.

UICollectionView.

Cells.

Headers and Footers.

If you’re showing a list of items, the chances are, you’ve used reuse identifiers.

tableView.register(CustomCellClass.self, forCellReuseIdentifier: "IdentifierForCustomCellClass")

Then, later, you’ve got to dequeue a cell of that exact same type, with that exact same identifier:

tableView.dequeueReusableCell(withIdentifier: "IdentifierForCustomCellClass", for: indexPath)

Even worse, this only gives you a UITableViewCell, not a CustomCellClass cell! This leaves you to either optionally unwrap and deal with a dead code path that should never be hit, or force unwrap, which always feels a little naughty!

guard let unwrappedCell = tableView.dequeueReusableCell(withIdentifier: "IdentifierForCustomCellClass", for: indexPath) as? CustomCellClass else { return UITableViewCell() // This will crash if hit, as the cell returned hasn't been dequeued from tableView } ---------------------------- let unwrappedCell = tableView.dequeueReusableCell(withIdentifier: "IdentifierForCustomCellClass", for: indexPath) as! CustomCellClass

If only there were a better way.

A tidier way.

Enter, ReuseIdentifiable.

tableView.registerCellClass(CustomCellClass.self) let typedCell = tableView.dequeueReusableCell(CustomCellClass.self) // Gives you a correctly typed cell

It’s that simple, and can easily be made to work with UITableView and UICollectionView for both cells and header and footer views. It integrates with existing APIs, removes String identifiers and removes casting – what more could you ask for?!

What’s that? A super simple implementation?

Well you’re in luck. At its core, ReuseIdentifiable is just a protocol defined by a single variable with a default implementation.

protocol ReuseIdentifiable: class { static var reuseIdentifier: String { get } } extension ReuseIdentifiable { static var reuseIdentifier: String { return String(describing: self) + "ReuseIdentifier" } }

Conformance becomes as simple as:

extension UITableViewCell: ReuseIdentifiable { }

As you can see, under the hood the String(describing:) function is used to get a String representation of the current object class. This gets ReuseIdentifier appended for clarity more than anything else; the class name itself would work fine.

From here, we can add extension functions to our list view of choice to make registering classes and dequeueing cells as simple as shown above:

extension UITableView { func register<C: UITableViewCell>(_ cellClass: C.Type) { register(cellClass, forCellReuseIdentifier: cellClass.reuseIdentifier) } func dequeueReusableCell<C: UITableViewCell>(_ cellClass: C.Type, for indexPath: IndexPath) -> C { return dequeueReusableCell(withIdentifier: cellClass.reuseIdentifier, for: indexPath) as! C } }

As you can see, there is still a cheeky little force-unwrap going on in the dequeueReusableCell function, but presuming you stick to one reuse identifier per class, there is literally no way the cell could be any other class.

So go forth and reduce the amount of String identifiers in your code!

A gist of all this code can be found on my GitHub page, here.