Architecture patterns are a heavily debated topic among software engineers.
Ultimately there is no right answer for every project and it depends on your needs. I did some research on the 7 most popular architecture design patterns in iOS and will share them will you in this article.
The 7 most popular iOS architecture patterns are:
Model-view-controller is the most popular design architecture in-app architecture design. It is the one Apple recommends and it’s easy to understand and use. In most code examples on the official Apple or Swift websites, applications are built using the MVC architecture. This is the default application architecture pattern used in all iOS, MacOS, and watchOS projects.
The controller in a model-view-controller architecture is responsible for many things. It receives all the view actions, handles all user interaction logic, dispatches changes to the model, receives changes from the model and lastly applies those changes to the view.
This results in the controller files being very large and difficult to break apart in the MVC model. Many developers think that is a downside of using a model-view-controller architecture. There’s a joke that MVC actually stands for Massive View Controller :).
The application starts by initializing the top-level view controllers. These view controllers then load and configure the views with the data from the model. The controller can either create its own models explicitly or access them through a singleton. If multiple controllers need to access the same model, a UIDocument can be used to make this easier.
In this architecture, the controller will receive updates either through delegates or user interactions with the view. Views have no knowledge about their controllers. When an update or change occurs, the view controller will update the model or modify its own internal state.
View changes occur after the controller has updated the model. After the controller has updated the model, another function in the view controller is listening to changes to the model. When changes are detected to the view model, the controller will make the appropriate changes to the view itself. This is a one-directional flow of data which makes it easier to debug and manage this code later on.
View states are stored either as properties of the view or the view controller. If a view action doesn’t change a model property, it can be stored on the view or the controller and doesn’t need to update the model.
Sadly, in an MVC model, the view controller is extremely tightly coupled with the rest of the application. The lack of boundaries and compartmentalization makes it more difficult to test.
Integration tests are typically used in MVC models as these are the easiest tests to write for this type of architecture.
Model-View-ViewModel separates a lot of the logic the view controller had to handle into a separate component called the view model. The view model is responsible for all the presentation logic. It is essentially an augmented version of the MVC architecture. This lets our view controllers be smaller and more manageable. Due to its similarity with Model-View-Controller, MVVM can be easily incorporated into an existing MVC app.
Model-View-ViewModel initialization is very similar to MVC. A difference is that the view controller now also initializes a view model. The view controller then binds each view to relevant properties in the view model.
The models are also no longer directly owned by the view controllers and instead owned by the view models.
The view controller receives updates or changes from user interactions or delegate calls, just like the MVC pattern. However, instead of the view controller changing its state or the model, it calls a function of the view model. The view model then either changes its internal presentation state or makes changes to the model.
In MVVM, the view controller does not observe the model. Instead, the view-model is responsible for listening to changes to the model. The view model handles model changes and updates its own internal state. The view controller is subscribed to the changes in the view model and will update the view accordingly when it detects those changes. Typically these are done using a reactive programming framework or other observation mechanism.
View states are either properties of the view themselves or stored in the view model. The view controller does not store any view state, unlike MVC.
Testability is a little easier in MVVM than MVC because the view model is decoupled from the view controller. This allows you to perform interface testing as opposed to integration testing.
Interface tests are a lot easier to write and maintain as you don’t need to setup a large amount of components to test a certain piece of logic.
The model-view-controller-viewstate architecture aims to build on MVC. It is similar to the MVVM pattern as well. However, the view controller still determines all model changes, unlike the view model in MVVM. It aims to separate out view state and make it more manageable.
The view controller now has to listen to both the view state and the model. This observation can be set through reactive programming. This means more notification work will be done through the view controller.
When a change comes in, the view controller updates the view state or the document model. All changes to the view hierarchy happens through either the view state or the document model.
The view controller observes the view state or the document view model and changes to the view occur through these observations.
The view state is explicitly separate from the view controller. The controller updates the view state and listens to changes from it.
For this architecture, testability is done via integration tests. However, it is slightly better than MVC as you can test the logic to the document model and the view model separately.
ModelAdapter-ViewBinder is a new, more experimental design pattern. It aims to get rid of the controller entirely and instead replace it with view binders and model adapters.
View binders are a wrapper class around the view class which provides bindings for changing the view with new information or emitting events based on user interaction.
View binders have dynamic bindings but they are immutable. This makes this architecture declarative. You define the bindings to the view in the view binder once and the bindings handle the data flow.
Model adapters are wrappers around the model can be described as reducers. Just like the view binder, it has a two-way binding that allows it to send events and receive updates.
Modal adapters and view binders are created before any view is made. Views are then created and bound to their respective view binders.
When the view emits actions, the view binder is notified. The view binder will then subsequently notify the model adapter. The model adapter will reduce this message and make the appropriate changes to the model.
When the model changes, the model adapter will emit a message to the view binder. The view binder will handle this message and the view will change accordingly.
View state is stored in the model. View state data flows the same way as changes to the model.
In this architecture we test our code by testing the view binders. The view binders can be tested to verify they behave correctly during a certain user interaction or when there is an update to the model.
The Elm Architecture, also known as TEA, is quite different from the model- x design patterns. In TEA, the model and view state are a single object. All updates to the model and view state are sent as messages to this single state object and are handled by methods called reducers.
Every change to the state results in a new virtual view hierarchy. This is a set of structs describing how the view should look. The benefit of this is that our views can be described as pure functions. The view is always calculated from the state object.
The way events flow in this architecture is unidirectional. If you’re familiar with Facebook’s Flux or Redux, it’s a very similar pattern.
The state is created on startup. The virtual view hierarchy is then created from the state and the runtime updates the real view based on differences between the real view and the virtual view hierarchy.
Virtual views can send messages when user interaction occurs. These are then passed to the state model and can update the store.
All view changes must happen as changes to the state object.
View state is a part of the state model.
In the elm architecture, you can just test that the state model produces the correct virtual view hierarchy when the state changes. Since views can be produced from pure functions, this makes testing simple. It allows us to test each part of the view hierarchy individually. This is one of the main benefits of using The Elm Architecture.
VIPER is an adaption of Clean Architecture for iOS applications. It stands for View, Interactor, Presenter, Entity, and Routing. VIPER is designed to build components around different user cases.
VIPER is a hard pattern to start with as it requires many different components. Teams that use VIPER almost always use code generators as the amount of boiler plate code required to use this pattern is quite large.
It’s an architecture that has many different components so let’s break it down.
The view displays what the presenter tells it to. It sends messages back to the presenter based on user interaction.
The interactor contains business logic on a per use-case basis.
This contains the view logic and view state. It changes the view based on messages sent from the interactor.
Entities are basic models that the interactor uses.
The router controls which view will be displayed and what the next view will be.
Component-based architecture is a newer to mobile app development. This has become very popular with frameworks such as React Native. Flutter also has a similar design with its use of widgets.
It will be interesting to see if this architecture gains more traction, especially in the Swift or native iOS development world.
What’s the easiest design pattern for iOS? Model-View-Controller is the best pattern for beginners to learn. Apple writes a lot of their learning material using this pattern and theres a strong community behind it.
My view controllers are too big, how do I make it more manageable? Consider using a design pattern such as model-view-view-model.