Model-View-Controller (MVC) in iOS: A Modern Approach

Many should know that MVC has been around for at least 30 years if not longer. Traditionally MVC was used to support apps that only ran on one device type i.e. the PC. In that time the POCO/POJO was the Model, the UI was the View and the code-behind class was the Controller.

However apps today may span multiple device types: PC, phone, tablet, watch, tv, game box. This is why the new level of abstraction is needed. As @rperes mentioned, it is necessary to separate your view business logic out of your UIViewController because that business logic may need to be re-used across multiple device types that may use different UI frameworks. In addition it allows your view business logic to be tested via unit tests.

In order to support multiple device types, it is necessary to understand that UIViewControllers (which are equivalent to code-behind classes) no longer fall under the “Controller” layer but are actually now part of the “View” layer in MVC . In the multiple device types scenario, your new “Controller” class should be clueless to what UI framework it is being coupled with.

It is this new level of abstraction that allows your business logic to be re-used across device types. Please be aware that “view business logic” is completely different than “UI framework logic”.

An example of “view business logic” is something like this: to edit an order, the user should have a security level of administrator. That business rule would be true regardless of whether the app was being executed on a phone, pc or iPad.

UI framework logic would be something like: “When the user edits an order the screen should do a flip animation and present the user with the order details”. The classes and frameworks used to accomplish the flip animation may vary across device types.

Examples such as this is why it is necessary for an extra layer of abstraction to exist between the business view logic and the UI framework logic. You shouldn’t have to duplicate the business logic across device types and thats why the new level of abstraction is needed.

Also, should models have logic? This is one of those things that devs go back and forth on. Models that only have properties are known as “anemic” models and many swear by this approach. I kind of take a middle of the road approach. I usually have a library that contains anemic models and then attach small utility type methods to them via extensions (emphasis on the phrase “utility type methods”). You shouldn’t put major business logic within your models. You also shouldn’t put data retrieval logic within your models. All of that is a big no-no.

The methods that should be attached to models should be very type specific and should be logic that will be needed across multiple controllers. A quick example is something like let’s say an order number looks like this: “17B-234-X33”.

And say you need a method to retrieve the 3rd and 9th character which is always a letter and signifies customer type - that would go in your Order model. Now let’s say you want to retrieve all orders for a particular date - that WOULD NOT go in your Order model. It would go into your data service. At least that’s how I do it :slight_smile:

Well based on the idea that you say that your UIViewController should be <130 lines of code then you are just moving your massive amount of code into another class. But now instead of 1 large class you have 1 large class and 1 small class. I suggest you combine them as Apple prefers and write smarter more reusable code that can be tested. If you really need to test large classes of business logic as it appears in your view (this is actually integration testing) then you probably should just use UI Automated testing. IMO if you are having massive view controller problems it is because you are lacking in creating proper management classes.

I have seen massive view controllers and they happen when people start using too many singletons and start getting lazy and just putting everything in one class because it’s there. Your solution will not solve that if you are just moving it from one class to another.

Can you point me where you feel I have a big un-testable class?

I think there is a contradiction in here. How can I combine them and then “write smarter more reusable code”?[quote=“valleyman86, post:22, topic:16452”]
If you really need to test large classes of business logic as it appears in your view
[/quote]

Where is this happening in the demo project?

My solution aims for small reusable and testable classes. I try to keep size in mind, but it’s not a rule written in stone. If something is big, by its nature, I will try to make it as small as possible, but if it’s not possible, so be it.

I am more than happy to continue this discussion else where: either via skype or email.

Dear Rui Peres,

as you said “the devil is in the details” and although the MVC-Pattern seems to be easy to understand, it really isn’t, when it comes to more complex apps. I’d like to ask you a fundamental question on that topic:

If you work with Interface Builder (as you are suggested to do so by Apple), you usually implement a lot of flow logic of the app in the UIViewControllerClass (e.g. via segues, via IBActions, embedding UINavigationControllers, 
). That somehow indicates for me, that the UIViewControllerClass is a ControllerClass (in the MVC-Pattern), that intermediates between the ModelLayer and den ViewLayer. And the UIViewController usually owns UIView-Elements on the one side but also modelObjects on the other side. This perfectly fits to the default behavior of xCode, when you start an new iOS-Application → the UIViewControllerClass ist the initial starting point of the app, initializes the UIViewElements and usually the MainModel (thats what you can see in lot of tutorials).

BUT NOW YOU SAID: UIViewControllerClass is part of the VIEW-Layer and so I am confused. That means you need a separate ControllerClass for doing the flow control. But which role does the UIViewControllerClass than have? Is it still the initial app entry point or should we start with the separate ControllerClass, instantiated in the AppDelegate? Or does the UIViewControllerClass hold (own) the separate ControllerClass, but not the model?
:slight_smile:
Thanks in advance !!
Robert / Germany

That’s for me the issue. The UIViewController is too coupled with the View layer, so it makes it very difficult to be tested, almost like a black box. You can see it sometimes, by people exposing internal methods and properties in order to test it, which I find fundamentally wrong. [quote=“lukesidewalker, post:25, topic:16452”]
And the UIViewController usually owns UIView-Elements on the one side but also modelObjects on the other side.
[/quote]

The UIViewController should never own model objects. You are pretty much skipping an entire layer and going from the View directly to the Model.

The initial start of the app is in the AppDelegate and that’s actually one of the reasons why I avoid Storyboards, for example. It makes it intrinsically difficult to test things.

The UIViewController is responsible for the life cycle of the screen (viewDidLoad, et all). He also responsible for customization, like navigation bar and others. Finally, it is responsible to hold an entity (that belongs to the Controller layer) and be the bridge between its own View (root view) and that entity.

Think about a UITableViewCell (where there is no UIViewController) with a button that fetches something or initiates an action (for example the like button in the FB app). Does it make sense for that Cell to communicate directly with the network layer? Well no, there must be an intermediate step between those.

The biggest issue I have, is this subtle thing, where you have the word “Controller” in UIViewController and suddenly the UIViewController becomes part of the Controller layer. I find this wrong:

  1. The Controller layer classes, shouldn’t care about who uses it.
  2. It shouldn’t be bound to a specific implementation.

The UIViewController is bound to a specific implementation: UIKit.

Rui,
I am pleasantly surprised in a positive way that there are projects/tutorials that do not use the story boards/nib files (it is not that I am against it, but testing is difficult). Like you mentioned underestimating dependency injection is very difficult to deal with, at a later point in the life cycle of the project. Hats off to you for your painstaking responses to each of the readers comments. Articles of this kind will reinforce good programming architecture. Expecting more of this kind in this site !!!
:joy:

Very informative article. Thank you. I always like reading posts about good archiecture in order to further my own knowledge.

I’ve gone back and forth with storyboards. I switched to using nib files to break down the UI to managable pieces. But it became overwhelming to added more code. So I reverted back to using storyboards.

I like your approach of doing everything by code. But my concern is auto layout code is a lot of verbose and can clutter up easily. I think I need to test this out myself.

@rperes THANK YOU for taking the time to write up and respond to the comments.

(caveat) I’m a UI/UX/Agile/Product manager with 20 years experience on high end creative/dev projects, simply trying to finally dip-my-toes into the basics of IOS development/gesture driven experiences; Hence trying my best to do the right thing and take seriously the subject of ‘architectural patterns’ from experienced communities to help in my school boy understandings. Your efforts in this post are really appreciated and peoples comments as well (thanks all!!!).

I appreciate theirs ‘no silver bullet’. I’d previously tried ‘my best’ to glean a ‘basic understanding’ from the topics posted below. However, having since stumbled over this welcomed post; I will follow this thread with interest, as I start to learn the ‘hard way’ and adapt ‘personal choices’ moving forward as my skills/understandings evolve. (Hence, curious any advice from parties who may have followed/tweaked any of these practices to aid further learning/understanding
)

Apple WWDC 2016: “Improving Existing Apps with Modern Best Practices”
https://developer.apple.com/videos/play/wwdc2016/213/
//Apples comment “Well, it’s not really new but a design pattern we’d like you to start using called Dependency Injection.”

Good iOS Application Architecture [EN]

//MVVM, MVC, VIPER
 so many acronyms, which architecture is the best? Let’s talk about the things that matter for good app architecture for iOS.

*## other resource - ‘Google titles’ (as I couldn’t post more than 2 links in this forum post as a newbie!!!)

iOS Architecture Patterns Demystifying MVC, MVP, MVVM and VIPER
//Feeling weird while doing MVC in iOS? Have doubts about switching to MVVM? Heard about VIPER, but not sure if it worth it? Keep reading, and you will find answers to questions above

Unidirectional Data Flow in Swift: An Alternative to Massive View Controllers
//Many iOS developers are familiar with the problem of the “Massive View Controller”


Coordinators Redux (flow coordinators)
//Three Problems: Overstuffed App Delegates, Too Many Responsibilities, Smooth/navigation flow.

Connecting View Controllers
//We refactor a storyboard to an alternative way of controlling the app’s flow. Our goal is to manage the view controller flow from a central place, so that view controllers don’t have implicit knowledge of their context.

Summary
As a ‘newbie’ reading this post/above; I see allot of favourable merits in what’s being discussed. The main difficulty/uncertainty I have is; taking the ‘right’ first steps in learning IOS projects (SpriteKit) from resources and awesome books from RayW teams - with a view to understanding ‘scalable architecture/testable/best practises’ within this side of IOS development. Personally speaking it’s really difficult to know if one is “setting sail, in the right direction” with a long term view of “mastery level”.

Speaking honestly, I get really confused when I TRY to understand Apples ‘example’ game/dev/stack projects, as the examples/structures seem to ‘muddy the waters’ in the more modern approaches to MVC discussions and go against information/advice I’d tried to glean from working with developers in real world projects who favour (right or wrong) DI, MVVM* (style*) code adopting single responsibility principles. Thanks in advance for ‘any comments’ or ‘book recommendations’ favouring separation/clean architecture approaches as a ‘newbie’.

Hi,

How do you handle supporting several screen sizes in code? Do you adjust auto layout constraints programmatically?

Hi Rui,

Storyboards and dependency injection without exposing the dependency: one solution promoted by Natasha in her blog is is to use an injectable protocol and a generic inject method. It is a pretty cool solution in the way that it standardises the whole process.

Thanks for sharing, this must be very convenient to do testing, btw can you demo the test part just for this example? would love to learn more!

+1 to seeing testing in action here.

Hi @lukesidewalker, actually, a UIViewControllerClass is an object which fulfills both the controller and view roles. Please see this section in Concepts in Objective-C Programming

Combining Roles
One can merge the MVC roles played by an object, making an object, for example, fulfill both the controller and view roles—in which case, it would be called a view controller. In the same way, you can also have model-controller objects. For some applications, combining roles like this is an acceptable design.

A model controller is a controller that concerns itself mostly with the model layer. It “owns” the model; its primary responsibilities are to manage the model and communicate with view objects. Action methods that apply to the model as a whole are typically implemented in a model controller. The document architecture provides a number of these methods for you; for example, an NSDocument object (which is a central part of the document architecture) automatically handles action methods related to saving files.

A view controller is a controller that concerns itself mostly with the view layer. It “owns” the interface (the views); its primary responsibilities are to manage the interface and communicate with the model. Action methods concerned with data displayed in a view are typically implemented in a view controller. An NSWindowController object (also part of the document architecture) is an example of a view controller.

Design Guidelines for MVC Applications offers some design advice concerning objects with merged MVC roles.

For this particular approach where components are made up of a Model, View Controller, and an actual Controller class, would the View Controller or the Controller be the communication node when communicating events between components? For example, consider Component A which presents, contains, or otherwise acts as a parent of Component B, and Component B must pass a message to Component A through a delegate. What class in Component A implements the delegate, and which class in Component B has an open delegate property that Component A can attach to?

Hi, I’m new and I didn’t finish to read this topic, but I try to download the example code and it doesn’t work with xcode 8, because it was made with swift 2 , then I update the code to swift 3 and I create a PR in GitHub. If you want Merge or comment I appreciate it.

Thx.

You were on the right track when you identified that the ViewController is in fact part of the view layer. Unfortunately, after that you just made a mess of things. You have created 3 separate classes with the word Controller in the signature that only differ by some meaningless infix (UI, View). The way apple intends people to develop is to use the controller for the logic for a feature/activity. The models provide services and data structure. The controller also takes input and updates the view. For obvious reasons this can lead to some terrible code. Many of the most basic software engineering principles are easily violated if we are not careful with this architecture. The problem is that people put too much emphasis on the view of the app. The model should stand alone entirely. That is, given an input, it should produce the required output or state. If you want to make sure that you are not putting too much responsibility in the view layer or the controllers then don’t build any GUI until you have implemented the rest of the feature. The GUI should essentially be tacked on to the side of the app. This is a little difficult because of how navigation is handled but it is possible. Once you have implemented the feature, you want to fill in the view layer and the controller layer. The view layer consists of any UIView subclasses and the UIViewController subclass. I usually do not add the Controller suffix to the view controller to avoid confusion. The view controller contains outlets for view properties and provides an interface to update the view. Because I use story boards, user interaction is also received here but is passed to a controller to be handled. The view and viewController is inherently untestable and application specific so it is not necessary to abstract the controller instance from the view. That is, I inject the controller instance into the ViewController directly. The controller is abstracted from the view using an interface as the controller could potentially be tested although it is somewhat of a poltergeist class as all it does is tell the model about input and update the view. The controller accesses unstable parts of the model through stable abstractions. Things like a DAL should always be abstracted. A controller, (whether its a viewController or not) should not make a network call!. Getters are not used as they are usually too implementation specific. Instead, the controller delegates for the model classes to receive data. What does all this get us? We have the applications functionality tucked away in the model. It does not know or care about the view. This makes the model testable. We have a controller that is notified of input from the view. The controller give the model input and listens for updates. When it receives updates, it tells the view through an interface. This means that the controller is testable (although it should have minimal logic other than control flow of input and output). The view is minimal. It has methods to update itself and passes user interaction to the controller.

Thanks for great informtive article, I love to read it again thanks.

Chris
Solution Analysts

This tutorial is more than six months old so questions are no longer supported at the moment for it. We will update it as soon as possible. Thank you! :]