Great article here (h/t: ManiacDev) on selecting an architectural pattern for your apps:
iOS Architecture Patterns: Demystifying MVC, MVP, MVVM and VIPER
Let’s define features of a good architecture:
- Balanced distribution of responsibilities among entities with strict roles.
- Testability usually comes from the first feature.
- Ease of use and a low maintenance cost.
Starts out going through the Massive View Controller we’re all familiar with,
…it might seem that Cocoa MVC is a pretty bad pattern to choose. But let’s assess it in terms of features defined in the beginning of the article:
- Distribution — the View and the Model in fact separated, but the View and the Controller are tightly coupled.
- Testability — due to the bad distribution you’ll probably only test your Model.
- Ease of use — the least amount of code among others patterns. In addition everyone is familiar with it, thus, it’s easily maintained even by the unexperienced developers.
Cocoa MVC is the pattern of your choice if you are not ready to invest more time in your architecture, and you feel that something with higher maintenance cost is an overkill for your tiny pet project.
Next, the MVP option is “Cocoa MVC’s promises delivered” by redefining the UIViewController as the View,
… Does this mean that Apple’s MVC is in fact a MVP? No, its not, because if you recall, there, the View is tightly coupled with the Controller, while the MVP’s mediator, Presenter, has nothing to do with the life cycle of the view controller, and the View can be mocked easily, so there is no layout code in the Presenter at all, but it is responsible for updating the View with data and state.
In terms of the MVP, the UIViewController subclasses are in fact the Views and not the Presenters. This distinction provides superb testability, which comes at cost of the development speed, because you have to make manual data and event binding…
- Distribution —we have the most of responsibilities divided between the Presenter and the Model, with the pretty dumb View…
- Testability — is excellent, we can test most of the business logic due to the dumb View.
- Ease of use —… the amount of code is doubled compared to the MVC, but at the same time, idea of the MVP is very clear.
Thirdly, No doubt you’ve heard at least in passing of MVVM and its enablers of the Reactive Cocoa ilk, which decouple similarly to MVP,
- the MVVM treats the view controller as the View
- There is no tight coupling between the View and the Model
In addition, it does binding like the Supervising version of the MVP; however, this time not between the View and the Model, but between the View and the View Model.
So what is the View Model in the iOS reality? It is basically UIKit independent representation of your View and its state. The View Model invokes changes in the Model and updates itself with the updated Model, and since we have a binding between the View and the View Model, the first is updated accordingly…
There is one bitter truth about reactive frameworks: the great power comes with the great responsibility. It’s really easy to mess up things when you go reactive. In other words, if do something wrong, you might spend a lot of time debugging the app…
Yes. Yes, you might. If you mix your FRP with Core Data, you will, we confidently predict.
- Distribution —…the MVVM’s View has more responsibilities than the MVP’s View. Because the first one updates it’s state from the View Model by setting up bindings, when the second one just forwards all events to the Presenter and doesn’t update itself.
- Testability —the View Model knows nothing about the View, this allows us to test it easily. The View might be also tested, but since it is UIKit dependant you might want to skip it.
- Ease of use —…in the real app where you’d have to forward all events from the View to the Presenter and to update the View manually, MVVM would be much skinnier if you used bindings.
Some other worth reading discussions on architecting with MVVM:
And lastly, we’d skipped past this VIPER thing up to now, but anything described as “LEGO building experience transferred into the iOS app design” must be fun, yes?
VIPER makes another iteration on the idea of separating responsibilities, and this time we have five layers. Topping the View,
- Interactor — contains business logic related to the data (Entities) or networking, like creating new instances of entities or fetching them from the server. For those purposes you’ll use some Services and Managers which are not considered as a part of VIPER module but rather an external dependency.
- Presenter — contains the UI related (but UIKit independent) business logic, invokes methods on the Interactor.
- Entities — your plain data objects, not the data access layer, because that is a responsibility of the Interactor.
- Router — responsible for the segues between the VIPER modules.
Basically, VIPER module can be a one screen or the whole user story of your application — think of authentication, which can be one screen or several related ones. How small are your “LEGO” blocks supposed to be? — It’s up to you…
- Distribution —undoubtedly, VIPER is a champion in distribution of responsibilities.
- Testability —no surprises here, better distribution — better testability.
- Ease of use —finally, two above come in cost of maintainability as you already guessed. You have to write huge amount of interface for classes with very small responsibilities.
To help with that “huge amount”, check out Generamba, introduced here :
… An average iOS developer creates only one class per screen. But for the one who uses VIPER that’s a moment of suffering. To be true – it’s a little longer than just a moment. Usually he has to create and fill with boilerplate code for around five classes, six protocols and five test-cases … That were the reasons to create our own code generator called Generamba. We got a highly extensible tool for a wide range of different code generation tasks though originally it was developed with just VIPER modules in mind.
Another application of the VIPER principles to simplifying view controllers is
Humble Object Pattern in Swift
For a side assist to the model view whatever patterns when networking is involved — and when isn’t it these days? — check out
Exploring MVC-N in Swift
… So, you cache the data, change the layer, and each one of the ViewControllers will stick it in the cache as they get data. When any other ViewController comes up, they can get the data out of the cache. No. This is bad.
This is what I refer to as the “anti-pattern”. I cannot count the number of times I have seen this, or done it myself. This is the pattern I am attacking. I am openly saying: please stop doing this. We want to write code, we want to see results right away, and we end up doing this. This is a problem…
That is what we should be doing at design time, not at the 11th hour when we are shipping tomorrow morning, and saying “I need to refactor all this”. When we have the UI/UX, we understand how the app needs to come together, and that is when we should be looking at it.
“I need data from the network. Where do I put that code to get the data out of the network?” This is where the MVC-N comes in…
For a completely different option, check out ReSwift (née ReduxKit/SwiftFlow), a Swift version of Redux:
Unidirectional Data Flow in Swift: An Alternative to Massive View Controllers
Redux is an alternative or a variation of the Flux framework that was developed at Facebook, and that Facebook now uses for most of their web applications. The main idea is that information always only flows in one single direction. We don’t have communication between individual view controllers, we don’t have individual delegator callback blocks. Information flow is structured and set in one very specific way…
Along the same Flux-inspired lines is Ziggurat iOS App Architecture.
Of course, if you have seriously simple needs, having a controller at all might be overkill:
Design Patterns in Swift: Document-View
The document-view pattern was once the preferred pattern used in Visual C++ development. Microsoft built the original Microsoft Foundation Classes around this pattern, in fact, back in the dark ages. It’s not used much today, but it’s useful when you have simple data management needs…
And finally, here’s a resource for even more deep diving into architectural patterns:
A Declarative Architecture for Swift Apps
Improve your iOS Architecture with FlowControllers
boilerplate: “Select the right architecture and functional reactive programming framework.”