Taming the Massive Controllers in iOS Part 1

UPDATE: Part 2 is now available

Mohammad Azam
Sep 20, 2016 · 5 min read

No matter how simple your iOS app may be you still have to conform to the MVC software architecture pattern. The MVC pattern spreads the responsibility of the application among Model, View and Controller. Unfortunately, most of the time the responsibility ends up on the shoulders of the controller. This practice results in what is known as Massive Controllers.

I presented “Taming the Massive Controllers in iOS” at IndieDevStock. If you would like to see my session video then buy the remote pass.

Massive Controllers are view controllers which do not abide by the Single Responsibility Principle. These controllers might be accessing data, calling web services, creating UI elements and doing other tasks which has no direct relation with the controller. This results in technical debt and maintenance nightmares.

The main focus of this post is to show you some techniques that can be used to tame the massive controllers. We will start with a very simple application called “Grocry”. Grocry application is responsible for keeping track of the shopping list. The interface for the Grocry app looks like the following:

Grocry App

Even though the app is extremely simple the code in the designated controller, ShoppingListTableViewController is quite heavy. Here is the fraction of code in segments.

override func viewDidLoad() {

The initializeCoreDataManager, which is responsible for initializing the CoreData is implemented below:

private func initializeCoreDataManager() {

The UIAlertView you see in the screenshot is displayed using the following code:

@IBAction func addShoppingListButtonPressed() {

And the list goes on and on and on..

The code above works without any problem. But looks can be decieving. Every time you add code to the above controller you are creating technical debt. This means everytime you are adding/removing/updating code in the view controller it is going to take longer and longer resulting in headaches down the road.

CoreData Stack

The most obvious refactoring you can perform is to move all the CoreData setup code out of the view controller and into a separate class. We will call that separate class “CoreDataManager” and it’s sole responsibility is to setup the CoreData stack.

class CoreDataManager: NSObject {

With CoreData setup moved to a different class we can simply initialize the it from inside the AppDelegate. The implementation is shown below:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

Much cleaner right! Not only that but now you can go in your ShoppingListTableViewController and delete all the code responsible for setting up the CoreData stack. Deleting code is always fun :)

DataSource:

At present all the data source methods are implemented inside the ShoppingListTableViewController. They are the delegate methods of the UITableViewDataSource. These methods include

override func numberOfSectionsInTableView(tableView: UITableView) -> Int

Instead of implementing the methods inside the ShoppingListTableViewController we can extract them out to a datasource. This way everything related to the datasource will be available in one place and the controller will be free of that responsibility.

ShoppingListDataSource is our new datasource class which is responsible for providing the necessary data to the UITableView control.

class ShoppingListDataSource<CellType :UITableViewCell>: NSObject,UITableViewDataSource, ShoppingListDataManagerDelegate {

As you can see in the code above ShoppingListDataSource conforms to the UITableViewDataSource protocol and implement all the required delegate methods.

The most important part of the ShoppingListDataSource is the initialization code which takes in multiple arguments.

init(manager :ShoppingListDataManager,tableView :UITableView, cellIdentifier :String, cellConfigurationHandler :(CellType,ShoppingList) -> ()) {

Apart from the manager argument which is of type ShoppingListDataManager, most are self explanatory. The manager is responsible for communicating with CoreData to retrieve the records and provide it to the datasource.

The ShoppingListTableViewController can now consume the datasource as shown in the implementation below:

class ShoppingListTableViewController3: UITableViewController, {

You can now remove all the code associated with NSFetchedResultsController from your ShoppingListTableViewController since the responsibility is moved to ShoppingListDataSource and ShoppingListDataManager classes.

The ShoppingListDataManager initialization code is shown below:

class ShoppingListDataManager: NSObject, NSFetchedResultsControllerDelegate {

As indicated previously, ShoppingListDataManager is responsible for communicating with CoreData through NSFetchedResultsController. The only parameter it needs is a reference to the NSManagedObjectContext.

By introducing the ShoppingListDataSource and ShoppingListDataManager we are already witnessing a lot of code improvements. Our ShoppingListTableViewController no longer contains datasource and NSFetchedResultsController delegate methods. The code is much nicer, leaner and easier to understand and change.

In the next part we are going to introduce Swift Extensions and how it can help to create reusable and maintainable code.

Download Code

Mohammad Azam

Written by

iOS Developer, speaker and educator. Author of ARKit for iOS Developers and Mastering ARKit for iOS Udemy course. Lead instructor at DigitalCrafts.