Tomasz Gebarowski

Protocol extensions and reuseIdentifier in UITableView

Inspired by protocol extensions in Swift 2 and reflection mechanism, I implemented a simple UITableView extension allowing to forget about reuseIdentifiers when registering and dequeuing UITableViewCells.

One may define a Reusable protocol that will specify a dynamic property named reuseIdentifier:

protocol Reusable {
    static var reuseIdentifier: String { get }
}

In Swift 2 we may extend that protocol and provide a default implementation. Because each reuseIdentifier should be unique with respect to the class we may use reflection mechanism to simply map a class name to a string.

extension Reusable {
    static var reuseIdentifier: String {
        let mirror = Mirror(reflecting: self)
        return String(mirror.subjectType)
    }
}

Note that String(mirror.subjectType) returns simply a type name of the class implementing Reusable protocol. Returned value has the following pattern:

ClassName.Type

Because Reusable protocol has already a default implementation of reuseIdentifier, it is possible to extend UITableViewCell and UITableViewHeaderFooterView to indicate that both classes implement our Reusable protocol:

extension UITableViewCell : Reusable {}
extension UITableViewHeaderFooterView: Reusable {}

Finally, we can provide helpers methods for registering and dequeuing our UITableViewCells.

extension UITableView {

    enum Type {
        case Cell
        case HeaderFooter
    }

    func registerClass<T: UIView where T: Reusable>(aClass: T.Type, type: Type) {
        switch (type) {
        case .Cell:
            registerClass(aClass, forCellReuseIdentifier: T.reuseIdentifier)
        case .HeaderFooter:
            registerClass(aClass, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
        }
    }

    func registerNib<T: UIView where T: Reusable>(aNib: UINib, aClass: T.Type, type: Type) {
        switch (type) {
        case .Cell:
            registerNib(aNib, forCellReuseIdentifier: T.reuseIdentifier)
        case .HeaderFooter:
            registerNib(aNib, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
        }
    }

    func dequeueReusableCell<T: UIView where T: Reusable>(aClass: T.Type, type: Type) -&gt; T {
        switch (type) {
        case .Cell:
            return dequeueReusableCellWithIdentifier(T.reuseIdentifier) as! T
        case .HeaderFooter:
            return dequeueReusableHeaderFooterViewWithIdentifier(T.reuseIdentifier) as! T

        }

    }
}

Note: The idea of reuseIdentifier defined in a Reusable protocol was based on the following Swift project, that I found interesting. I pushed it a bit further to avoid hardcoding reuseIdentifiers in code.

Code presented in this blog post can be found on my github account. It’s more like an experiment, not yet used in any app.

If you like this post, please follow me on twitter: @tgebarowski