MVC that is not massive

Alok Subedi
4 min readAug 6, 2021

--

I started my iOS development following Apple’s food rating app. iOS development was in an early stage in my country, so I was a solo iOS developer. MVC was my single source of truth. All the code should go in either of Model, View or Controller. My controller handled everything. That was how most of the tutorials taught.

My MVC rules were simple.
1) Model is a dumb data structure.
2) View is storyboard. Use separate file for view only if the inspector is not enough.
3) One ViewController is one screen. It handles everything that happens when you are on that screen, network call, persistence, business logic, rendering view, getting UI feedback.

We will look into how I used to write my earlier iOS apps and refactor it into multiple MVCs. You can download the starter project from here to follow along.

Project Overview

This is the UI of a login view we are creating. It is a pretty standard login view with app icon, app name, login form, third party login and sign up button.

UI for the login view

Open the project. We will work with LoginViewController. SignupViewController and HomeViewController are for navigation and DummyThirdPartyLogin fakes login with Facebook and Google.

LoginViewController

Let’s see what’s in this 236 lines of code.

  1. Creation, addition and laying out views
    Apple’s ViewController is both view and controller so it is logical that LoginViewController creates and handles its views. There are first 53 lines for creating views and 178 to 236 (58) lines for addition and giving them auto layout constraints.
  2. UITextFieldDelegate
    It acts as UITextFieldDelegate to handle what to do when user taps return on keyboard for usernameTextField and passwordTextField.
  3. Hide keyboard when tapped anywhere on view
    It adds tap gesture to view and handles hiding of keyboard.
  4. Login
    Main job of LoginViewController. It first checks if username and password is empty, validates password is 8 characters or long, creates URLRequest with username and password, makes asynchronous URL request, handles the response. So much task in one function.
  5. Login with third party
    It configures third party login and handles the response.
  6. Show error
    It shows error in form validation or login failure.
  7. Navigation
    It creates the viewController and present them. Home view on successful login and signup view when user taps sign up button.

These are the responsibilities of LoginViewController and seven reasons it is massive. Lets refactor it now.

Sevaral Smaller MVCs

My understanding of MVC like many other fellow iOS developers, was “One Screen = One MVC”. Oh, I was wrong. We can have separate smaller MVCs, as small as one view, one model and one controller, and compose them on one big MVC.

How to separate them?

See if a part can exist as a single screen.

We can clearly see that login form, including login button, can exist as a single screen. Let’s create a separate MVC for login form.

  • Create a new class LoginFormViewController, and copy the views necessary for login form along with creator methods.

We got some errors, let’s fix them.

  • Copy text UITextFieldDelegate extension from LoginViewController to LoginFormViewController. It asks for ‘dismissKeyboard’ function, copy that as well.
  • Now move the ‘login’ function to LoginFormViewController. We don’t have ‘showError’ and ‘openHomeView’ methods. Error label is in LoginViewController and navigation to HomeView too, so we cannot just copy those methods. Delegate pattern to the rescue. Lets create a LoginFormViewControllerDelegate and use it to notify errorOnLogin or loggedIn.
  • Add and layout the views. Remeber to pin the top and bottom of viewController’s view to username signInButton. I have also removed trailing and leading space, so the view remains tight and open for spacing in parent view.
  • Clean up LoginViewController. Remove the views moved to LoginFormViewController, and addition and layout of those views, UITextFieldDelegate, and login function.
  • When error free, add LoginFormViewController as a child.
  • Make it LoginFormViewControllerDelegate to show error and open homeViewController.
  • Add and layout the loginFormView.

Now we have a smaller MVC that handles login and is UITextFieldDelegate, two fewer responsibilities for LoginViewController. We added two extra responsibilities of creating LoginFormViewController and acting as its delegate. We can use composer and dependency injection instead.

Exercise

Now that we separated a LoginFormViewController, how about we create a MVC for google and facebook login? Try it.

What next?

We just separated a big MVC to smaller MVCs, yet we are a long way to have a cleaner code. We can move dependency creation to dependency injection, navigation responsibilities to the router or coordinator, URLSession task to api service, view creation, addition and layout to separate view classes and other techniques.

The idea is to reduce responsibilities from massive viewController by learning one technique at one time.

--

--