A Better Way to Generate Reuse Identifiers in Swift and iOS

A Better Way to Generate Reuse Identifiers in Swift and iOS

iOS offers an elegant way to improve table view and collection view performance by cell reuse. These scroll views may have millions of cells, but iOS only stores the visible cells in memory and reuses them as they go out of view. This enables OS to save on memory and improve the app's performance.

However, before we leverage this feature, we need to assign the reuse identifier to cells in the table view. How can we do that? In general, I have seen people using some hardcoded on-the-fly value as a reuse identifier like this,


final class MyCell: UITableViewCell {
	......
    ....
    ..
}

....

tableView.register(MyCell.self, forCellReuseIdentifier: "cell")
tableView.register(MyCell.self, forCellReuseIdentifier: "cell1")
tableView.register(MyCell.self, forCellReuseIdentifier: "myCell")

However, this practice raises a few problems,

  1. We are using hardcoded strings which is not a good programming practice
  2. There is no way to keep track of all the cell reuse identifiers used in the code
  3. If you're using generic names for reuse identifiers, it is possible that you may end up using the same reuse identifier string for multiple cell types
  4. Every time you're registering cells with table or collection views, you need to think hard and come up with a suitable reuse identifier

In order to avoid the performance hit and improve our code, let's take a look at another approach to generate cell reuse identifiers.

Creating a protocol for reusable view

First, we will create a protocol for reusable views such as table view cells or collection view cells. The protocol will have a single property namely reuseIdentifier which reusable cells will implement to generate reuse identifiers.


/// A protocol for reusable views such as Table view cells to provide
/// reuseIdentifier for cell reuse
protocol ReusableView {
    static var reuseIdentifier: String { get }
}


Conforming to Protocol

Reusable views are those views that are reused in the scroll view subclasses - such as table views and collection views.

We will have our UITableViewCell and UICollectionViewCell objects to conform to this protocol and derive reuse identifiers from their class names.

For table view cells,


final class MyTableViewCell: UITableViewCell {

}

extension MyTableViewCell: ReusableView {
    static var reuseIdentifier: String {
        return String(describing: self)
    }
}


For collection view cells,


final class MyCollectionViewCell: UICollectionViewCell {

}

extension MyCollectionViewCell: ReusableView {
    static var reuseIdentifier: String {
        return String(describing: self)
    }
}

Next time you're registering cells in the table or collection view, directly call thereuseIdentifier property on cell type instead of using a hardcoded value. Less typing and better naming were achieved for known reusable views.


tableView.register(MyTableViewCell.self, forCellReuseIdentifier: MyTableViewCell.reuseIdentifier)

collectionView.register(MyCollectionViewCell.self, forCellReuseIdentifier: MyTableViewCell.reuseIdentifier)

💡
In order to avoid future bugs and API abuse, you can also introduce a linter rule which will throw a warning if you use different reusable view types while registering a cell to a scroll view subclass. For example, cases like this will be caught before they are observable in the app.

tableView.register(MyTableViewCell.self, forCellReuseIdentifier: MyCollectionViewCell.reuseIdentifier)

Bonus Content

In the future, when Apple introduces a new reusable view type, you can avoid a boilerplate code by simply conforming its subclass to ReusableView and use reuseIdentifier like we used before.


final class MyNewReusableView: UITimCookViewCell {

}

extension MyNewReusableView: ReusableView {
    static var reuseIdentifier: String {
        return String(describing: self)
    }
}


Summary

So that was all about a better way to generate and use the reuse identifier for reusable views in Swift and iOS. Hope this post was useful in avoiding lots of boilerplate and confusing code in your app. Do you have your own tips to avoid boilerplate in the iOS app? Do share and let me know on Twitter @jayeshkawli