Today I would like to discuss how to present UITableViewCell contents by using enums. A simple switch statement can help us with displaying data on our tableView. What scenario would I like to focus on?

To be more precise, I have created a simple schema of this screen:

By the way, have you had a chance to use Sketch for prototyping? Quite a nice tool!!!
The question is: what is the best proposal of enum configurations and functions, which can help us with displaying data when using tableView:cellForRowAtIndexPath: method for an example above?
My first proposal was:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
enum PlaceInformation: Int { // Used on UISegmentControl case General case More } enum GeneralInfo: Int {//Cells for General Info Segment (Tab) case Description case HowToGoHere case Hotel } enum MoreInfo: Int {//Cells for More Info Segment (Tab) case CurrentWheather case InterestingPlacesNearby } class PlaceInformationViewController: UIViewController, UITableViewDataSource { var tableView: UITableView? override func viewDidLoad() { super.viewDidLoad() tableView = UITableView() ... } let selectedTabIndex = 0 //selected segment on UISegmentedControl func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = UITableViewCell() let row = indexPath.row let selectedInformationTab: PlaceInformation = PlaceInformation(rawValue: selectedTabIndex)! switch selectedInformationTab { case .General: guard let informationCellType = GeneralInfo(rawValue: row) else { break } switch informationCellType { case .Description: print("Setup Description Cell") case .HowToGoHere: print("Setup HowToGoHere Cell") case .Hotel: print("Setup Hotel Cell") } case .More: guard let informationCellType = MoreInfo(rawValue: row) else { break } switch informationCellType { case .CurrentWheather: print("Setup Wheather Cell") case .InterestingPlacesNearby: print("Setup InterestingPlacesNearby Cell") } } return cell } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } } |
Some statistics of the above code:
- Lines of code of tableView:cellForRowAtIndexPath: function: 28
- What did SwiftLint say?

My intuition prompted that it can be done better:). So I have started thinking of how to refactor the code.
First refactoring
So here we go with first refactoring. Below is my to do list to make this enum better:
- Remove white spaces according to SwiftLint
- Reduce cases in enum to one line
- Use nested enums
- Necessarily I wanted to check if enum associated values can improve code metrics. I don’t know why, but I hoped that it will improve my situation:)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
enum PlaceInformationCellModel { // returned cell depends on selectedTab and tableview section case Header(selectedTab: PlaceInformation, section: Int) enum PlaceInformation: Int { // Used on UISegmentControl case General case More } enum PlaceHeader { case Description, HowToGoHere, Hotel, CurrentWheather, InterestingPlacesNearby static let allGeneralHeaders = [Description, HowToGoHere, Hotel] static let allMoreHeaders = [CurrentWheather, InterestingPlacesNearby] } var value: PlaceHeader { switch self { case .Header(let tab, let sectionIndex): switch tab { case .General: return PlaceHeader.allGeneralHeaders[sectionIndex] case .More: return PlaceHeader.allMoreHeaders[sectionIndex] } } } } class PlaceInformationViewController: UIViewController, UITableViewDataSource { var tableView: UITableView? override func viewDidLoad() { super.viewDidLoad() tableView = UITableView() ... } let selectedTabIndex = 1 //selected segment on UISegmentedControl func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cellModel: PlaceInformationCellModel = PlaceInformationCellModel.Header(selectedTab: PlaceInformationCellModel.PlaceInformation(rawValue: selectedTabIndex)!, section: indexPath.row) let header = cellModel.value let cell = UITableViewCell() switch header { case PlaceInformationCellModel.PlaceHeader.Description: print("Setup Description Cell") case PlaceInformationCellModel.PlaceHeader.HowToGoHere: print("Setup HowToGoHere Cell") case PlaceInformationCellModel.PlaceHeader.Hotel: print("Setup Hotel Cell") case PlaceInformationCellModel.PlaceHeader.CurrentWheather: print("Setup CurrentWheather Cell") case PlaceInformationCellModel.PlaceHeader.InterestingPlacesNearby: print("Setup InterestingPlacesNearby Cell") } return cell } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } } |
Is it better? I hoped so, so I have asked my friends about feedback:
-
Lines of code of tableView:cellForRowAtIndexPath: function: 20
-
What did SwiftLint say?

- What did codebeat say?: 4.00 GPA
Second refactoring
I still had a feeling that the code was too complicated. I thought that getting rid of associated values would make it simpler.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
enum PlaceInformation { case General, More enum PlaceHeader: Int { case Description, HowToGoHere, Hotel, CurrentWheather, InterestingPlacesNearby static let allGeneralHeaders = [Description, HowToGoHere, Hotel] static let allMoreHeraders = [CurrentWheather, InterestingPlacesNearby] } var placeValues: [PlaceHeader] { switch self { case .General: return PlaceHeader.allGeneralHeaders case .More: return PlaceHeader.allMoreHeraders } } } class PlaceInformationViewController: UIViewController, UITableViewDataSource { var tableView: UITableView? override func viewDidLoad() { super.viewDidLoad() tableView = UITableView() } let selectedInformationTab: PlaceInformation = .General func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let header = selectedInformationTab.placeValues[indexPath.row] let cell = UITableViewCell() switch header { case .Description: print("Setup Description Cell") case .HowToGoHere: print("Setup HowToGoHere Cell") case .Hotel: print("Setup Hotel Cell") case .CurrentWheather: print("Setup CurrentWheather Cell") case .InterestingPlacesNearby: print("Setup InterestingPlacesNearby Cell") } return cell } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } } |
Feedback:
- Lines of code of tableView:cellForRowAtIndexPath: function: 18
- SwiftLint:

It seems that the easiest solution is the best:

Third refactoring
I would like to leave the next iteration of refactoring for you. What would you change to improve the solution?