Slim UIViewController through UITableView DataSource & Delegate Encapsulation
UITableView and UICollectionView are two of the main user interface elements that drives an iOS app when presenting list of data. They are very performant and efficient, because Apple has put many optimisations such as efficient memory usage by enqueue/dequeque mechanism of Cell when the user scrolls through the list. Recently, Apple also adds Cell prefetching to improve the performance when user performs fast scrolling.
MVC (Model View Controller) is the basic architecture that Apple recommends to start with when building an iOS app. When we have a UITableView in our UIViewController class, we usually just make our UIViewController conforms to the UITableViewDataSource and UITableViewDelegate protocols and assign it as the datasource and delegate of the UITableView. This pattern can make our View Controller become so bloated and became very massive because of the data source and delegate methods implementation.
View Controller that has too many responsibilites to handle creates tight coupling between components, this tight coupling make testing become harder to create. Based on the SRP (Single Responsibility Principle), a Class should only has one responsibility, and we should move other responsibilities to another classes so our system become modular that make unit test of a component is easier to create and test.
So what can we do to improve our View Controller, avoid Massive View Controller, and create less coupling between components in our app to make Unit Testing become easier to create?.
One of the thing we can use is by encapsulating the UITableViewDataSource and UITableViewDelegate into separate DataSourceProvider Class. The DataSourceProvider conforms to the UITableViewDataSource and UITableViewDelegate protocols. We also passed a DataManager object as an instance property for the DataSourceProvider when we initialise it. The DataManager encapsulates the Model for the application. The DataManager provides method to get list of items, add item, delete item, or update item. The backing store for the data can be a plist file, Core Data, or even in memory array.
The DataSourceProvider implements the UITableViewDatasource methods such as providing number of section, number of rows in a section, cell dequeue and configuration at specific indexPath. The DataSourceProvider then asks the DataManager for the number of items and getting specific item properties to display in a UITableViewCell.
To integrate with the ViewController, we store the DataSourceProvider and ItemManager as instance properties in ViewController. When ViewDidLoad is invoked we instantiate the DataManager and DataSourceProvider, then we assign the DataSourceProvider object as table view data source and delegate property.
Using this pattern, we have separated the responsibilities for the ViewController into separate objects, DataProvider class that handles the displaying the cell and responding to user action in a cell inside UITableView, DataManager class that encapsulates the model for the application. With this, we can create separate Unit Test for the ViewController, DataSourceProvider, and DataManager class and keep our View Controller slim and less coupled that leads to a much clean code in our application. To see all the code for this article, you can visit the project GitHub page.