Easy to Use Cell Reuse Extensions
One of the things I’ve always found annoying in iOS development is worrying about cell reuse identifiers. First you have to figure out how you want to add them to your cells. Static properties? An enum with all of your cells? Something else? Then you have to make sure to add that same reuse identifier to your xib. And if that wasn’t enough duplication you have to use it again when you go to dequeue a cell for use.
We can make this a little better with some clever use of protocols and extensions so you only have to copy the name of the cell’s class to your xib and you’re done.
ReusableView
One thing every custom cell class has in common is they all have a class name. You can access that name by using String(describing: self)
. We want to use the class name as the reuse identifier, so the first thing we’ll need to do is add a computed property to UITableViewCell and UICollectionViewCell to return the class name.
A good way to go about this is to create a new protocol called ReusableView
and give it a default implementation containing our new reuse identifier property.
protocol ReusableView: class {}
extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(describing: self)
}
}
To avoid repeating code and to extend where we can use the reuseIdentifier
property, I added the default implementation to UIView
. Now everything that is a UIView
automatically gets a reuse identifier we can use.
To add this same identifier to cells all you need to do is add extensions on cell classes, adopting the ReusableView
protocol.
extension UITableViewCell: ReusableView { }
extension UICollectionViewCell: ReusableView { }
Dequeueing Cells
Now that every cell automatically has a reuse identifier, we can make some generic helper functions on UITableView and UICollectionView to make dequeueing cells possible without needing to provide a reuse identifier.
extension UITableView {
func dequeueReusableCell<T: UITableViewCell>(forIndexPath indexPath: IndexPath) -> T {
guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
}
return cell
}
}
extension UICollectionView {
func dequeueReusableCell<T: UICollectionViewCell>(forIndexPath indexPath: IndexPath) -> T {
guard let cell = dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
}
return cell
}
}
The fatalError
calls are a helpful way to make sure you aren’t shipping code with broken cells, but you can handle that case however you’d like!
Now that we have our helper functions set up all you need to do to dequeue a cell is:
let cell: MyCustomCell = tableView.dequeueReusableCell(forIndexPath: indexPath)
No optionals, no identifiers. The only thing you do still need to remember to do is add the cell’s class name as the reuse identifier in your xibs.