The other day, I reduced a view controller in my iOS app by approximately 100 lines – from about 400 to about 300, and it felt great.
Just reduced a View Controller by ~100 lines (400 -> 300) by moving its tableView dataSource to another class. Feels good.
— Josh Brown (@jtbrown) March 11, 2014
I’m so tired of working with view controllers that do way too much, and it looks like I’m not alone. In fact, that post is what inspired me to get the table view’s data source out of my view controller and into another class. From the post:
A View Controller may not implement any extra *Delegate or *DataSource protocols…
So the other day, I refactored my view controller’s table view data source to another class, dropping it to ~300 lines from ~400, making it much cleaner and easier to understand. Here’s what I normally do in my view controller (and, in fact, what pretty much everyone does – so don’t feel bad if you do it, too):
self.tableView.dataSource = self;
Clearly, this violates the proposed rule above, and it’s part of the reason our view controllers are so overwhelmingly large. So here’s how I did the refactoring…
First, I initialized the dataSource in my view controller’s init method. (This allows me to inject another dataSource in my unit tests.) Here’s the relevant part of my init method:
- (id)init
{
// ...
self.userDataSource = [[UserDataSource alloc] init];
// ...
}
Of course, this required me to have a UserDataSource, so I created that, moving all my UITableViewDataSource methods (–numberOfSectionsInTableView:
, –tableView:numberOfRowsInSection:
, and –tableView:cellForRowAtIndexPath:
in this case) to the UserDataSource class. I practiced TDD as I built my UserDataSource, so now I have automated tests to ensure that the UserDataSource does what I want it to do and continues to do what I want it to do.
So back in the view controller, in viewDidLoad, I set the tableView’s dataSource to the one I instantiated in the init method.
- (void)viewDidLoad
{
// ...
self.tableView.dataSource = self.userDataSource;
// ...
}
And in doing so, I was able to remove 100 lines from my view controller – all the work I needed to do to get the table view’s number of sections, number of rows in section, and creating its cells. And now that that’s all in a separate data source class, my view controller can focus on updating my view and responding to actions – and forget about doing things it never should’ve been doing in the first place, like supplying data to the table view.
Is this perfect? Far from it. My view controller still violates other rules described in the SRP & iOS article, and it’s still 300 lines long – far bigger than it should be. But it’s a lot better than it was before.
If you’re struggling with the 900-line view controller problem, try moving your tableView’s dataSource to another class. It’s a great, relatively simple first step to reducing size and complexity in your view controllers.
Update: Here’s some sample code that demonstrates moving a table view data source into another class. You can browse the code on GitHub – look at IDTViewController.m and IDTBooksAndMoviesDataSource.m. Here’s the view controller diff – in this case, we’ve shortened the view controller by 20 lines and two responsibilities it shouldn’t have had (1. networking and 2. table view data source).
Enter your email below and grab my advice, tips, and tutorials on iOS development designed to help you become a master of iOS development – and never worry about whether your iOS chops are getting old, crusty, stale, and irrelevant again.