
MVP abbreviation can be interpreted in different ways, but the article is not about sports.
The network has a large number of articles on architectural patterns for iOS and Android developers, and MVP in particular. Sometimes a pattern is considered in the context of both platforms. Someone chooses this pattern to improve the testability of their code, someone uses it mainly to separate the presentation code from the model. Also there are solutions that use MVP to unify the platform code, provided that the developers in the company own these technologies. But common words and conclusions are sometimes not enough for a pragmatic developer. When designing commercial applications, inevitably many details arise that the overall architectural concept cannot reveal, and it cannot be said that there is a single canonical solution.
')
In the article I will try to describe the situations that mobile developers often encounter on real projects, and when it is really worth thinking about switching to an architectural pattern more complicated than “One UIView Controller (Activity) to rule them all”. Or better said, when it will be profitable for us. The following discussion focuses on the trade-off between time and development complexity in the realities of “ordinary” projects that mostly come to be assessed and developed.
Introduction
Most mobile projects for native SDKs are now client-server applications. In turn, such applications can vary greatly in system complexity and lifetime. There are “one-day” applications for work within one event or an annual holiday, after which nobody needs them. There are serious projects that require a large percentage of code coverage of tests, to ensure stable operation when changes are made by different team members.
What projects are “normal” can be determined by the following criteria:
- number of screens: 5-20 pieces, or as many screens should be in the first version;
- The application must be designed for two main mobile platforms: iOS and Android;
- authorization is present;
- there is a caching system using, as a rule, a local database;
- not only GET, but also POST, PUT, DELETE requests are made, and these requests make changes to the cached data downloaded in the previous step;
- The app will expand.
Comparing MVPs with default patterns
Consider the simplest pattern, namely iOS MVC with a passive model, which is essentially a rigid bundle of presentation layers and a model where all the logic is in a UIViewController. A similar approach is obtained on Android, if you write your leadership projects from video tutorials for beginners or from most training sites.

If we compare it with MVP, the main difference is the class Presenter, in which we bring the logic of event handling, data formatting and the View control. By View, we mean a layer that includes the child classes UIViewController, Activity or Fragment and their combination with classes of various controls. Thus, we “unload” classes similar to the UIViewController from those duties that they should not be engaged in, leaving only the initialization code for views and animations inside.

As is often the case, during the development of the first version of the application, additional functionality is added that is associated with insufficient study of the TOR. Sometimes the changes are so radical that you have to rewrite most of the work already done. The most common situation when asked to add an additional field to the entity and display the formatted information. Let us see which classes we change in the project when adding a new UI element that displays a formatted date:
- using MVC with a passive model:


Obviously, we have complicated the system by introducing the MVP pattern, since the number of classes that have undergone a change by simply adding a new property of the entity has increased. Separately, it is worth noting that, when implementing MVP, they create a separate interface for View and try to make sure that Presenter has as little code as possible associated with the platform and there are no direct links to specific heirs of UIViewController, Activity or Fragment, because the temptation is to write the presentation code directly to the Presenter.
For small and weakly subject to change projects, the approach without a clear separation of the presentation from the logic business is, in my opinion, the best approach, since it significantly reduces development time. A small project is usually written by one person, and the amount of code is not difficult for another team member to figure out the task in a few hours and make changes if required.
In search of a compromise
A logical question arises, why complicate things?
First, in order to clearly distribute the responsibility between the classes. This is especially true if you are a member of the development team. It is important to understand that no programmer thinks the same. For example, the date formatting code from the example may end up both in the table cell and in the controller, as well as in the code of the class responsible for mapping data received from the server.
In addition to formatting, there are quite a few such details in your project. You can give a more complex example of displaying in the list item a composite view model that is collected in parts from cached data. We transfer all the work on formatting and preparing the model for presentation to Presenter, so there is no ambiguity in where to format the data.

Do not confuse the presentation model in this article with the ViewModel from MVVM, the name only indicates the entity storing the formatted and ready to be shown in the View information.
When using the usual approach, we would most likely place the model code for the cell in the heir of the UIViewController or Activity (Fragment), which resulted in an increase in the code in the class, in which there is a lot of work related to the presentation.
Writing “useful” integration tests also becomes possible with the introduction of MVP, where the main object of testing is Presenter. Of course, using only unit testing, you can test the components used in the application, which are God-objects and the heirs of UIViewController or Activity (Fragment), but this will not be quite effective.
Consider the moment associated with the expansion of the project. Additional features on the project may not come immediately after the release of the first version, so it is important that developers adopt conventions on how to write code. MVP in this case is a set of rules that can be clearly described in the following diagram:

Small adjustments to the chart:
- For Android, it is not relevant to store the reference to Presenter directly in the Activity, so in practice we use Retained Fragment and base classes for the Activity and the usual Fragment, which describe the logic of initialization and retrieval of the already created Presenter.
Lot of codepublic abstract class BasePresenterActivity<P extends BasePresenter> extends AppCompatActivity { private static final String PRESENTER_FRAGMENT_TAG = "presenter_fragment_tag"; private P mPresenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getPresenter().bindView(this); } @Override protected void onPause() { super.onPause(); if (isFinishing()) { PresenterFragment presenterFragment = (PresenterFragment) getSupportFragmentManager() .findFragmentByTag(PRESENTER_FRAGMENT_TAG); if (presenterFragment != null) { getSupportFragmentManager().beginTransaction().remove(presenterFragment).commit(); } } } @Override protected void onDestroy() { if (!isFinishing() && mPresenter != null) { mPresenter.unbindView(); } super.onDestroy(); } protected P getPresenter() { if (mPresenter != null) { return mPresenter; } PresenterFragment fragment = getPresenterFragment(); if (fragment.isPresenterSet()) { mPresenter = fragment.getPresenter(); } else { mPresenter = createPresenter(); fragment.setPresenter(mPresenter); } return mPresenter; } protected abstract P createPresenter(); private PresenterFragment getPresenterFragment() { PresenterFragment presenterFragment = (PresenterFragment) getSupportFragmentManager() .findFragmentByTag(PRESENTER_FRAGMENT_TAG); if (presenterFragment == null) { presenterFragment = new PresenterFragment(); getSupportFragmentManager() .beginTransaction() .add(presenterFragment, PRESENTER_FRAGMENT_TAG) .commit(); } return presenterFragment; } }
public class PresenterFragment extends Fragment { private BasePresenter mPresenter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } public <P extends BasePresenter> P getPresenter() { return (P) mPresenter; } public <P extends BasePresenter> void setPresenter(P presenter) { mPresenter = presenter; } public boolean isPresenterSet() { return mPresenter != null; } }
public abstract class BasePresenter<V> { protected V mView; public void bindView(V view) { mView = view; } public void unbindView() { mView = null; } }
- Interfaces are introduced only for View and Service for integration testing of Presenter logic. In order to create truly effective tests, Presenter must be aware of all the events generated by the View, and View should not respond to them, even if the event handler contains only one method call.
- Earlier in the article, the class EntityToViewModelFormatter was not mentioned. Its introduction is due to the fact that on different screens you may need information from the same entities, which is formatted in the same or similar way. Obviously, this class is optional and is used only where it is needed.
Using MVP, we can also divide the development of one screen into several people. At least the View and Presenter bundle is well separated from the Model. Typically, developers associate Model with an entity in MV * patterns. I am more impressed by the attitude to the model layer as a data service, which Presenter uses to receive and update information. We use the service as a facade and combine requests to the API, into a database and application settings.
This reveals the only drawback of the described MVP variation, namely that the service grows within the same class file. If in iOS we can use categories and extensions to separate one large class-service, then in Java it is necessary to split the service into classes of sub-services to work with a specific screen.
Diagram of splitting a service into several sub-services for Android If you have no more than 20-25 API methods, then the code remains readable within a single file, provided that it is correctly formatted and commented. For the first version of the product, you can describe all the logic for receiving and sending data within the same class. But do not get carried away, as technical debt will increase over time.
Since the presentation can be separated from the model, it will not be difficult for you to show your work to the customer, even if with static data, in case the backend is not yet ready or is unstable. Fortunately, there are many ways to lock the server part. If an independent team is involved in the backend, if you have agreed documentation, you can be more or less sure that when this service appears, it will not surprise you which fields are sent to the API, and you will have time to debug the part related to formatting data for the presentation.
Without documentation, I would still stop the development at the stage of creating the design of the screens and the output of test data based on the presentation models. In this case, you will also get a less functional interface suitable for a demo, but most importantly you will not spend a lot of time changing entities to fit them under a real API, as Presenter acts as an adapter in which data from the service will be converted to formatted view model data.
Versatility
Another advantage of using the MVP pattern is its versatility for iOS and Android. If you have a large number of projects being developed for both platforms, then the Presenter logic will be universal, and if application development takes place in turn, the adaptation on the other platform will be faster and more predictable, since the logic code in Presenter is almost the same. Moreover, at the stage of starting work on adaptation for another platform, ready-made test cases will already be available. Of course, if you bother to spend time and write them.
The next advantage of using the MVP pattern is the division of developer resources for different platforms. For example, iOS signor can write part of the code under Android at the junior or middle level and be useful in this case so that he does not have to redo everything. I myself am not a supporter of “universal soldiers”, since it is better to know one technology well than several at the junior or middle level. I haven’t come across practicing senior developers (not timblids or technical directors) for both platforms, but I won’t argue that such professionals exist. The main reason for the shortage of such people, in my opinion, is that nowadays it is very difficult to keep track of the development of both technologies. Even if iOS and Android borrow some designs from each other, the implementation is still different under the hood. But for small teams and “normal” projects, the approach using a non-core developer is quite adequate. Many iOS programmers would like to try their hand at Android and vice versa. You can give this “legionary” a write part of the code associated with the Model or Presenter. It would also be nice if a person could write a simple UI. But tasks with complex UI elements with animation, optimization, profiling and multithreading, with the exception of service API calls, of course, have to be solved by an already more experienced specialized developer.
An interesting case is when testers find a bug on two platforms at once. It turns out that if the bug is in logic, then the task can not be divided into several developers, but given to repair one person. At the same time, he will not have much time to understand the logic of different platforms, since they use the same architectural solutions.
Comparison with other famous architectural patterns
There is another very important question: why was MVP chosen as the basis, and not patterns based on pure architecture or MVVM? At the beginning of the article we tried to concentrate on what we are dealing with “ordinary” projects, which we would like to write quickly, but with proper quality and the possibility of long-term support.
Pure architecture will suit large teams that have experienced programmers on their staff for a specific platform, simply because the code has to be written many times more than in MVP, and this code needs to be properly positioned in a specific module.
In MVVM, we have to rebuild the logic of thinking on the declarative. MVVM is also often associated with Rx * libraries, which programmers should be familiar with, and this immediately puts a limit when searching for a new developer, or you consciously spend time learning it. In this regard, MVP is more transparent and has less restrictions on developer skills.
findings
We turn to the conclusions. The advantages that we were able to identify for the MVP pattern, if we look at it from the side of the benefits of implementation in the development:
- The present separation of the presentation logic from the model, in contrast, for example, from Apple MVC;
- A clear division of responsibility between classes:
- View - drawing, animations, transitions between screens;
- Presenter - formatting, reaction to events, presentation logic and View control;
- Model - working with loading data by API, extracting data from the database and caching it;
- Almost universal implementation of Model-View-Presenter interfaces on mobile platforms for “ordinary” projects;
- Presenter integration testing;
- Separation of tasks in a team as part of developing a single screen into presentation tasks (along with the Presenter) and models;
- The ability to display an interactive demo to the customer, without a working backend. When a real service appears, we don’t throw out all the code that we wrote, but adapt the API;
- The ability to effectively attract non-core developers from the team to write code on another platform;
- Expansion and addition of new features will not cause the effect of “someone else's code” when a person returns to the project development after a long break due to a set of simple rules for placing code in strictly defined modules.
The disadvantages include not so fast development speed as when using Apple MVC or a similar approach for Android. Also in the concretely presented variation of MVP there was deliberately an emphasis on one common class-service due to simplicity, but this would require some techniques for its separation.
Summing up, we can say the following: if you have a small team of experts ready for implementation of projects for the main mobile platforms, you need universal architectural rules, as well as the possibility of parallelizing tasks within the screen and the need to show a demo to a customer with partially working functionality, if sprints are practiced, then MVP is definitely your choice. An additional bonus is the convenient support for the project, which should not lead to the phrase “but if we had written everything again” and situations where more than to spend time sorting out the code of one huge UIViewController or Activity the feature itself.