This is an overview of how we architect native iOS/Android apps at TangoCode. Without going into the specifics of Cocoa or Java(Android) we’ll share some insights about our architecture process that are common or apply for both iOS and Android platforms.
Why does architecture matter?
According to the definition of Architecture by Wikipedia, architecture is the process of planning, designing, and constructing buildings. This is exactly what we do when we create software. We use our creativity to design our apps and put together all the pieces we need to make a product that is aesthetically pleasing. Since this post is about software architecture, the word “product” refers to the code we write and “aesthetically pleasing” means it has to be enjoyable for the developers who are either analysing, improving or scaling the codebase.
Let’s start from the beginning with the architecture that 100% of mobile developers have used at least once in their life, that’s right… it is MVC. This architecture has been around for a long time and it’s the default architecture that Xcode or Android Studio will put in place once you start a new project. The variation of MVC that we get when we create a native project works really well when you are doing your first steps in mobile development. It provides us with a guideline and an idea of where to put our code without worrying too much and going all over the place creating files and folders. BUT as we all know, software products tend to grow and once a mobile app under MVC starts growing our initial architecture turns into the infamous well known Massive View Controller architecture, where Controllers can have easily more than 500 lines of code, which doesn’t sound like clean code at all, right?
The reason why these Controllers become humongous is because the MVC architecture that our IDEs help us to put in place is a variation of the real MVC. In the Massive View Controller architecture, the controllers have too many responsibilities which goes against of the Single Responsibility Principle where software components focus only in one responsibility. Instead in MVC, controllers have to deal with all the configurations and interactions from the UI in addition to the business logic that we have to implement in our mobile apps.
It doesn’t take long to realise there is something with this MVC that doesn’t allow you to enjoy the code you write, especially if you are writing an application with a rich UI and a lot of business logic. This is why we started moving some code out of these huge controllers based on the Single Responsibility Principle. The idea here is to have the business logic in one place and everything related to the UI control (UIKit, android.view) in another. In MVP this place is the Presenter, where basically you will have all your business logic isolated from any UI framework or platform-specific libraries with just a reference to the view which is the one who is aware of all the UI elements that we have on the screen and has a reference to its presenter as well. Here the responsibility of the presenter is to update the View with the data and update the Model state after any user interaction. It’s like a mediator between the View and the Model.
- This could be seen as the first step of breaking the MVC architecture and moving to a more code-distributed scheme. When we analysed MVP, we found it easy to understand and easy to adopt.
- It doesn’t require any extra framework/technique to bind the View with the Presenter. Basically, the presenter will never see the view as a UIViewController or Activity, it will only see a View and will know when it has to interact with it through an interface.
- One downside of this approach is the coupling between the View and Presenter, where they have to know each other. This coupling will be tackled with the MVVM architecture.
I mentioned there is a strong relationship between Presenter and View in MVP, where they have to know each other creating a coupling that could be avoided. With MVVM (Model-View-ViewModel) this relation goes away and responsibilities change. The view becomes more isolated and only reacts to user interactions and data updates coming from the ViewModel, this ViewModel replaces the presenter and it has to handle everything related to business logic without keeping any relationship with the view and handling only business logic related tasks.
There is an interesting and challenging aspect of this architecture in the way the ViewModel communicates with the View. This is especially true with Android and iOS because in platforms like with the Windows phone, the way ViewModels react and update the Views is built-in.
There are two ways to have the ViewModels interacting with the view without creating a two-way reference. The first way is through the observer pattern (KVO in iOS) where any time the viewModel is updated, the view will react to this changes and will update itself. The second way and the most interesting one is using all the power of Functional Reactive Programming (FRP). Thanks to frameworks like RxJava, RxSwift, RxJS, etc. we can enjoy the benefits of this programming concept, where basically you have a stream of Data reacts whenever there is a new signal emitted over this stream. Translating this to our MVVM architecture in mobile, this is how it looks using FRP:
- We recommend switching from MVP to MVVM in order to have a better understanding of these architectures. Also, the transition from MVP to MVVM is way smoother than transitioning from MVC to MVVM.
- MVVM can be implemented without using any extra frameworks and we recommend doing at least a “HelloWorld” of MVVM using regular observers or KVOs before moving to MVVM with FRP.
- Initially, we came across to Rx* frameworks looking for alternatives to do asynchronous calls inside our mobile apps. It turns out that Rx* is just the implementation of an amazing concept called Functional Reactive Programming that we encourage everybody to learn and use. It helps you to communicate components inside your app that are reacting to signals emitted through a data stream.
Clean (VIPER in the iOS World) Architecture
The Clean Architecture is another enterprise architecture transferred from traditional web development to the mobile development world. We see this architecture as an MVP with two new members: Interactor and Navigator. In Clean the Presenter is only in charge of transforming the data that will be displayed by the View, this data is processed by the Interactor also known as Use Case, which is the one that obtains the data from the Data Layer and applies the business logic rules, and Navigator is the component that handles the navigation between screens (Activities, UIViewControllers, etc..).
What Clean mainly does is embrace the code distribution and single responsibility principles by splitting the whole application into three layers: Data Layer, Domain Layer, and Presentation Layer. Where Naturally View, Routing, and Presenters belong to the Presentation Layer, Interactor belongs to the Domain Layer, and Entity belongs to the Data Layer.
- In order to embrace a Clean Architecture without adding unnecessary coupling between layers, it is highly recommended to develop it with the dependency injection pattern in mind. Dagger for Android is a great framework for dependency injection in Android.
- This architecture produces the cleanest code, developers will find a well organized and structured project easy to understand. However, there is a trade-off when writing new features, it can become easy to get lost with the number of classes that need to be created for a simple functionality.
- In the long run, one of the main benefits of this architecture is the maintenance. It will be extremely easy to test, fix bugs, and understand the functionalities that have been implemented.
- VIPER (View Interactor Presenter Entity Routing) is the equivalent of Clean in iOS with some slight changes, especially on the Routing side. More detail is provided in the references section.
The more we distribute the code between abstraction layers the more testable and clean it will be; however, there are trade-offs as I mentioned before based on our experience with The Clean architecture in mobile. Our recommendations are to try to test each architecture and try to see which one fits better for your specific problem. For instance, if I need to do a quick and dirty demo or prototype, I wouldn’t worry too much about the architecture and I would adopt the default MVC; but if I need to build an app that needs to be well tested from the UI to the business logic rules, I would start thinking about abstractions, layers, and code distribution.
After studying and trying these different options for our mobile apps, we adopted MVVM with Rx as the default architecture. However, we are aware there is no a silver bullet architecture and they have to evolve along with the technology and the programming language.