HandsAppMVP: iOS architecture for studio development outsourcing

image

Good code starts with architecture, and iOS apps are no exception. There are many standard patterns, but the purpose of this article is not to talk about them, but about the experience of adapting one of them and developing your own. We called this adaptation HandsAppMVP.

image

In iOS development, the architecture primarily determines the organization of classes and dependencies for one specific ViewController. However, the central component can be not only he, but simply UIView. The choice depends on the specific task.

Architecture comparison


There are several standard architectural templates for iOS: MVC, MVP, MVVM, VIPER (links to the description of each can be found at the end of the article).

Choosing an architecture for development, we identified the main parameters that it should correspond to: development speed, flexibility, and a low entry threshold. Next, we took up the comparison of three well-known architectures with these parameters in mind (the MVC iOS community template has long been buried due to gross non-compliance with single responsibility).

For an outsourcing team, development speed is especially important. VIPER is the most complex and “slow” architecture; development is faster using pure MVP or MVVM, as they have fewer components.

Flexibility means the painless addition or removal of functionality in the application. This parameter strongly correlates with the development speed at all stages of the application life, except for the initial one. Flexibility is also closely related to the simplicity of testing - automatic tests give the developer confidence that he will not break anything, and help to avoid bugs. Classical MVP is poorly covered by tests, especially if you do not use the class interfaces discussed below. MVVM from the point of view of testing also has poor performance, because testing reactive code takes much longer. VIPER is great for writing tests because it respects the principle of sole responsibility as much as possible and classes depend on abstractions.

And the last parameter that we considered is the entry threshold. It shows how quickly new developers (first of all, the jones) penetrate into architecture. Here MVVM using third-party reactive libraries (RxSwift, PromiseKit, etc.) takes an honorable last place for obvious reasons. VIPER is also a rather complex architecture due to the large number of components. MVP has the lowest entry threshold.

After weighing the pros and cons, we came to the conclusion that we need something as simple as MVP, and as flexible as VIPER. So the idea was born to create their own architecture based on them - HandsAppMVP.

Expand MVP


The main components of our architecture are Model, View, Presenter. They perform the same functions as in the classical MVP according to the well-known scheme:

image
[Scheme of the classical MVP]

Here and below, each interaction component (blue square) in the diagrams is a class whose lifetime coincides with the lifetime of the View. A solid arrow indicates ownership of another object, a strong link, and a dotted arrow indicates a weak link. With weak references, we prevent circular dependencies and memory leaks.

Interfaces


First, we added the ViewInput and ViewOutput interfaces to this classic schema. We took into account the fifth principle of SOLID - the principle of dependency inversion. It is more likely not an addition, but a refinement for MVP. Dependence on abstractions helps to get rid of the strict connectedness of components and allows you to write tests normally. The scheme taking into account the interfaces looks like this:

image
[Adding ViewInput and ViewOutput interfaces] A

small rectangle is an interface.

An attentive developer will ask, where are the interfaces for Model? Now we turn to them.

Work with data


The data model in mobile architectures is a collective concept. A standard example: an application knocks on the network to interact with the server, then saves data in CoreData for offline work, writes some simple information to UserDefaults and stores the JWT in Keychain. All this data that is being interacted with makes up the Model.

The class that is responsible for interacting with the data container of a particular type, we call the data service. For each container (remote database, local database, UserDefaults, etc.), a service class is added to HandsAppMVP that interacts with the presenter. Now you can also add input / output interfaces for each data service:

image
[Adding services for working with data]

Not every service class needs to be connected to the presenter using an interface, as, for example, when using Moya. Moya is an open-source network library. Moya provides a ready-made service class (MoyaProvider), and when writing tests, we do not have to create a mock object that replaces ApiProvider. Moya provides a special test mode, when turned on, MoyaProvider does not knock on the network, but returns test data (more details can be found here). In this case, the presenter does not refer to the MoyaProvider abstraction, but to the implementation. And we get feedback from this service using closures. An example implementation can be found in the demo project.

This example is more an exception than a rule, and shows that adherence to SOLID is not always the best solution.

Navigation


We consider navigation in the application as a separate responsibility. For her, HandsAppMVP uses a special class - Router. The router contains a weak link to the View, with which it can show a new screen or close the current one. Router also interacts with the presenter using the RouterInput interface:

image
[Adding a component for navigation (Router)]

Component assembly


The last addition to the classic MVP that we use is Assembly, a collector class. It is used to initialize the View and other components of HandsAppMVP, as well as to implement dependencies. Assembly contains the only public method - `assemble () -> UIViewController`, the result of which is the desired UIViewController (or UIView) with the necessary dependency graph.

We will not show Assembly on the architecture diagram, since it is not connected with MVP components and its life cycle ends immediately after they are created.

Code Generation


To save time, we automated the process of creating HandsAppMVP classes using Generamba. The templates used for Generamba can be found in our repository. An example config for Generamba is in the demo project.

As a result of generating a specific screen, we get a set of classes that corresponds to the HandsAppMVP scheme, a set of unit tests for creating and implementing components, and also a template class for presenter tests.

What happened


If you compare Head-to-Head HandsAppMVP and VIPER, you will notice that they are very similar and the first differs only in the absence of the Interactor component. But, getting rid of the layer between the services and the present (the interactor), as well as simplifying the interaction with the network using Moya, we received a noticeable increase in the development speed.

We advise you to pay enough attention to architecture at the design stage in order to avoid global errors, disputes with customers and torment of developers in the future, and instead lead the development process competently and predictably.

Remember that any architecture may not be suitable specifically for your project, so do not rush to blindly cling to ready-made templates and successful stories of their application. Do not be afraid to develop and apply your solutions - they can become more valuable and flexible for you than ready-made ones.

In conclusion, we will recommend some good articles on the architecture of iOS applications that helped us understand the intricacies and make a choice:

  1. Architectural patterns in iOS
  2. iOS Swift: MVP Architecture
  3. Analysis of VIPER architecture on the example of a small iOS application on Swift 4
  4. Implement MVVM on iOS with RxSwift

SurfStudio 's open-source documentation was also very helpful and inspired .

Finally, we are putting a link on the DEMO project , written in HandsAppMVP, which we have repeatedly mentioned in the article.

All Articles