11 Making Table View Cells Autoconfigurable

I don’t know if autoconfigurable can be found in the dictionary, but I’m going to use it anyway because it best describes what we’re going to do in this chapter. In the previous chapter, we refactored the SettingsViewController class. In the tableView(_:cellForRowAt:) method, we create an instance of type SettingsRepresentable?. The view controller manually configures each table view cell, using a view model. But why can’t the table view cell take care of its own configuration?

All the table view cell needs to do is ask the view model for the values it needs and populate itself with data. This is very different from handing a model to a view, which certainly isn’t what we intend to do. The table view cell won’t know about the model, it will simply use the interface of the view model we give it.

Updating the Settings Table View Cell

To make this work, we need to make some changes to the SettingsTableViewCell class. Open SettingsTableViewCell.swift and define a method named configure(withViewModel:). The method accepts one argument of type SettingsRepresentable. In the body of the method, we set the text property of the mainLabel outlet and we update the accessoryType property of the table view cell.

SettingsTableViewCell.swift

 1 import UIKit
 2 
 3 class SettingsTableViewCell: UITableViewCell {
 4 
 5     // MARK: - Type Properties
 6 
 7     static let reuseIdentifier = "SettingsCell"
 8 
 9     // MARK: - Properties
10 
11     @IBOutlet var mainLabel: UILabel!
12 
13     ...
14 
15     // MARK: - Configuration
16 
17     func configure(withViewModel viewModel: SettingsRepresentable) {
18         mainLabel.text = viewModel.text
19         accessoryType = viewModel.accessoryType
20     }
21 
22 }

Updating the Settings View Controller

All that’s left for us to do is updating the SettingsViewController class. Open SettingsViewController.swift and navigate to the tableView(_:cellForRowAt:) method. The view controller no longer needs to configure the table view cell. Instead, we invoke the configure(withViewModel:) method of the table view cell and pass in the view model. I’m sure you agree that this looks quite nice.

SettingsViewController.swift

 1 func tableView(_ tableView: UITableView, cellForRowAt indexPath: Ind\
 2 exPath) -> UITableViewCell {
 3     ...
 4 
 5     if let viewModel = viewModel {
 6         cell.configure(withViewModel: viewModel)
 7     }
 8 
 9     return cell
10 }

Notice that the view controller is still involved, but its role is very limited. By implementing the configure(withViewModel:) method and handing a view model to the table view cell, we’ve slightly deviated from the Model-View-ViewModel pattern we discussed in the introduction of this book.

But that’s fine. Views need to be dumb. They shouldn’t know what they’re displaying. That still applies to the SettingsTableViewCell class. The table view cell only uses the interface of the view model to configure itself.

Protocol-Oriented Programming

I want to highlight the role of the SettingsRepresentable protocol in the story. The SettingsRepresentable protocol serves two purposes. The most obvious task of the SettingsRepresentable protocol is defining an interface. Because the view models we defined earlier conform to this protocol, the SettingsTableViewCell class only needs to have the ability to handle an object of type SettingsRepresentable.

But the SettingsRepresentable protocol performs a more important but less obvious task. It adds a layer of abstraction. The SettingsRepresentable protocol ensures that the SettingsTableViewCell class doesn’t need to know about the view models we defined (SettingsViewTimeViewModel, SettingsViewUnitsViewModel, and SettingsViewTemperatureViewModel). That’s an important advantage and that’s the beauty and elegance of protocol-oriented programming.

Conclusion

The settings view controller is a simple view controller, but I hope you can see the potential of the Model-View-ViewModel pattern in simplifying view controllers. The settings view controller is very focused. All it does is manage its view and subviews and handle user interaction. That’s the primary role of every view controller, and this applies to MVVM as well as MVC.

In the next chapter, we apply what we’ve learned in the past two chapter to the week view controller.