Display heterogeneous data in a table view

Share This Post

Share on facebook
Share on linkedin
Share on twitter
Share on email

One of the common developments in mobile applications is showing information using tables. This task can easily be done when you have a collection of homogeneous entities but it gets trickier when this collection has n-number of different entities. In this article, I’m going to show an example of how to display heterogeneous data in a table view using the adapter pattern. The example exposed is a messaging app with two types of messages; texts and images.

Display heterogeneous data in a table view

Display heterogeneous dataWhen we face the problem of displaying heterogeneous data in tables, the straightforward solution is to use pattern matching in the method “cellForRowAtIndexPath” to decide what cell we need to use to display the current entity in the collection.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let currentItem = itemsArray[indexPath.row]
            switch currentItem {
            case let item as TextMessage:<
                 let cell = tableView.dequeueReusableCell(withIdentifier: "TextMessageCell", for: indexPath) as! TextMessageCell<
                 cell.configure(viewModel: item)
                 return cell
            case let item as ImageMessage:
                 let cell = tableView.dequeueReusableCell(withIdentifier: "ImageMessageCell", for: indexPath) as! ImageMessageCell
                 cell.configure(viewModel: item)
                 return cell
                 fatalError("unsupported cell for type: \(currentItem)")

There are several drawbacks with this solution:

  • It is not scalable, meaning the switch cases will grow every time we need to display a new type of entity.
  • We are repeating the call to the method configure(viewModel:) that could be part of an interface for the cell.
  • Since we do not have a strictly typed number of entities, we need to provide a default case for the switch.

The Adapter Pattern

The adapter pattern is used to transform one interface into another that our system will use instead.

Let’s see a solution using the adapter pattern to make it scalable and avoid repeating code.

The solution will have 2 components:

  • Adapter interface
  • Adapter collection holder (Adapter map)

First we need to create an adapter interface, this interface will have basic cell configuration attributes and the configure.

protocol PostTableViewAnyCellAdapter {
     var reuseIdentifier: String { get }
     var preferredHeight: CGFloat { get }
     func configure(cell: UITableViewCell, with viewModel: Post)

The implementation of that interface will receive generic types for both model and cell.

class PostCellAdapter: PostTableViewAnyCellAdapter where CellType.ViewModelType == ViewModelType {
        var reuseIdentifier: String {
            return CellType.reuseIdentifier
        var preferredHeight: CGFloat {
            return CellType.preferredHeight
        init(tableView: UITableView) {
            register(into: tableView)
        func register(into tableView: UITableView) {
             switch CellType.registerMethod {
             case .nib(let nib):
                 tableView.register(nib, forCellReuseIdentifier:
             case .classReference(let cellClass):
                 tableView.register(cellClass, forCellReuseIdentifier:
      func configure(cell: UITableViewCell, with viewModel: Post) {
           let typedCell = cell as! CellType
           let typedViewModel = viewModel as! ViewModelType
           typedCell.configure(viewModel: typedViewModel)

Then we create a class that will hold a collection this adapter interface. This class will be called the Adapter map.

struct PostsAdapterMap {
               typealias InternalMapType = [String: PostTableViewAnyCellAdapter]
               private var internalMap = InternalMapType()
               subscript(viewModel: Post) -> PostTableViewAnyCellAdapter {
                   let key = String(describing: type(of: viewModel))
                   return internalMap[key]!
               mutating func add<CellType, ViewModelType>(adapter: PostCellAdapter<CellType, ViewModelType>) {
                   let key = String(describing: ViewModelType.self)
                   internalMap[key] = adapter

Now that we have all the components needed, we can use it in our view controllers by simply creating a new instance of that Adapter map class.

var tableAdapter = PostsAdapterMap()

To register the cell adapters to the table view we create instances of the Adapter using a pair of UITableviewCell subclass and a model:

let textAdapter = PostCellAdapter<TextMessageCell, TextMessage>(tableView: tableView)
tableAdapter.add(adapter: textAdapter)

Finally, our new implementation of the method “cellForRowAtIndexPath” will look as follows:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       let currentItem = itemsArray[indexPath.row]
       let currentAdapter = tableAdapter[currentItem]
       let cell = tableView.dequeueReusableCell(withIdentifier: currentAdapter.reuseIdentifier, for: indexPath)
       currentAdapter.configure(cell: cell, with: currentItem)
       return cell

We are no longer configuring cell view classes directly using pattern matching but instead using adapter interfaces that will receive the models and the recycled cell views. The adapter map will use the appropriate method for the given model to configure the current cell

How does this solve scalability? If we need to display another type of message in this chat application, let’s say a video message, we only need to create the cell view with it’s configure method, define the model and add it to the adapter map. The adapter map will handle everything and therefore, our table view datasource methods will remain unchanged.

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe To Our Newsletter

Get updates from our latest tech findings

About Apiumhub

Apiumhub brings together a community of software developers & architects to help you transform your idea into a powerful and scalable product. Our Tech Hub specialises in Software ArchitectureWeb Development & Mobile App Development. Here we share with you industry tips & best practices, based on our experience.

Popular posts
Free PDF with Software Architecture Interviews

Have a challenging project?

We Can Work On It Together