AtelierClockwork

Sanding Down Rough Edges

February 3, 2016

An Interesting Start for Hopefully Very Useful Code

Like most Swift developers working with UIKit I got annoyed with the fact that every single time I dequeued a cell from a UITableView working with anything that’s no just a UITableViewCell has to be cast using as?, and that registering classes for tables isn’t particularly smooth either. As a rough idea of how to get some type safety in the absolute lightest way possible, I wrote this:

protocol AutomaticDequeue {
}

extension AutomaticDequeue where Self: AnyObject {

    static var reuseIdentifer: String {
        return NSStringFromClass(self) as String
    }

}


extension UITableView {

    func registerCellClass<CellClass: UITableViewCell where CellClass: AutomaticDequeue>(cellClass: CellClass.Type) {
        registerClass(cellClass, forCellReuseIdentifier: cellClass.reuseIdentifer)
    }

    func registerHeaderFooterClass<HeaderFooterClass: UITableViewHeaderFooterView where HeaderFooterClass: AutomaticDequeue>(headerFooterClass: HeaderFooterClass.Type) {
        registerClass(headerFooterClass, forHeaderFooterViewReuseIdentifier: headerFooterClass.reuseIdentifer)
    }

    func cellForIndexPath<CellClass: UITableViewCell where CellClass: AutomaticDequeue>(indexPath: NSIndexPath) -> CellClass {
        guard let cell = dequeueReusableCellWithIdentifier(CellClass.reuseIdentifer, forIndexPath: indexPath) as? CellClass else {
            fatalError("Could not dequeue cell with identifier \(CellClass.reuseIdentifer)")
        }
        return cell
    }

    func headerForIndexPath<HeaderFooterClass: UITableViewHeaderFooterView where HeaderFooterClass: AutomaticDequeue>(indexPath: NSIndexPath) -> HeaderFooterClass {
        guard let header = dequeueReusableHeaderFooterViewWithIdentifier(HeaderFooterClass.reuseIdentifer) as? HeaderFooterClass else {
            fatalError("Could not dequeue header with identifier \(HeaderFooterClass.reuseIdentifer)")
        }
        return header
    }

    func footerForIndexPath<HeaderFooterClass: UITableViewHeaderFooterView where HeaderFooterClass: AutomaticDequeue>(indexPath: NSIndexPath) -> HeaderFooterClass {
        guard let footer = dequeueReusableHeaderFooterViewWithIdentifier(HeaderFooterClass.reuseIdentifer) as? HeaderFooterClass else {
            fatalError("Could not dequeue footer with identifier \(HeaderFooterClass.reuseIdentifer)")
        }
        return footer
    }

}

this adds a handful of really neat things, first off by adding conformance to the AutomaticDequeue protocol the class is then used to generate the re-use identifier automatically based on the class name. It also adds simplified class registration, which ends up looking like table.registerCellClass(MainMenuCell). After cells are registered, the can be dequeued with calls like let cell: MainMenuCell = tableView.cellForIndexPath(indexPath). The compiler can infer the correct class, and therefore the correct re-use identifier from the return type.

This is the kind of solution that makes me think I’m starting to scratch the surface of generating useful solutions with generics, particularly figuring out ways to add useful functionality if all of the correct conditions are in place, and to not get in the way otherwise.