iOS architecture patterns: A guide for developers

NOTE: This article is primarily for iOS developers with advanced skills and who are willing to master alternative architectural approaches to build next-level quality apps. In this article we explore all iOS architecture patterns in detail. 

Every iOS developer is familiar with issues related to product testing, code refactoring and support via ViewController. The latter is also known as a Massive View Controller. In search for solutions, we’ve delved into profound investigation of programming patterns for iOS. At some point you realize even this is not enough and time comes for iOS architecture patterns. So we analyzed top-5 patterns putting them to test in real-life projects. 

It is important to understand that architectural patterns are not the solution for all problems. They only describe approaches to design mobile applications. Details of realization depend and vary. That said, in this article we are covering the following patterns:

  • Classic MVC
  • Apple’s MVC
  • MVP
  • MVVM
  • VIPER

Get free estimation for your mobile app

Post your project or request a dedicated team - we'll quickly match you with the right experts.

Post project

Classic MVC

Model View Controller appeared in the programming language Smalltalk-80 in the late 70-ies. But since then a lot of time has passed, there have been many different interpretations of MVC. Though its initial idea gradually became forgotten, first, let’s discuss the classic MVC in a very close to the original interpretation. The main problem that must be solved by MVC – a clear division of between components responsibilities into Model, View, and Controller.

Model – a set of classes that encapsulate data of a specific subject area, their validation algorithms. In the classic MVC a model also includes processing logic ( “business logic”). There are two types of models: active and passive. The active model is able to notify about changes in its state (usually by the Observer Pattern). Classic MVC implementation is considered to be the active model. The Model doesn’t know anything about the View and Controller and can operate independently. This requirement plays an important role when it comes to testing.

View – a class that is responsible for graphic (though not necessarily) display of data. In classical MVC the View has a direct access to the Model only in reading mode. The View should not directly change the state of the Model. This is the responsibility of  the Controller.

Controller – responds to external stimuli. As a reaction to external stimuli, Controller performs some logic, including the change of the state of the Model. However, the Controller does not react to the View directly and also doesn’t keep its state. The Controller is not a mediator between the View and the Model; also it is not responsible for transferring data from the Model to the View.

Classic MVC scheme

Principles of classic MVC

There is some external event (e.g. a user presses the key, remember we are in the 70-ies). The event goes into the Controller, which decides how to process it. The Controller, for instance, can change the state of the Model (by calling its methods) but in no case the state of the View. Only the model affects the View directly.

If the Model’s state is changed, it notifies the View about the changes, and View reads the new values, and then is redrawn if necessary (the View observing the Model). While MVC successfully copes with its tasks in console mode, the graphic interface and a mouse become more popular. The user can now directly interact with the View, and the View generates events which, in theory, should be processed by the Controller. Classic MVC is changing.

To build a graphic interface different IDE, which already contain ready graphic components (Widgets, or Document View), appear. The task of the developer – to build a hierarchy of widgets and redirect the events from them to required classes. Therefore, in contemporary interpretation, the View is a hierarchy of widgets.

Graphic components are complex. For example, a usual button (UIButton), from the UIKit library, can contain different text for each state of the buttons (e.g. for the “highlighted” – “I am in a highlighted state”, and for the “selected” – “I am in a selected state”) . You can also set the color of the text for each state. It can be configured directly in the visual editor. The developer does not write a single line of code (although he can).

So, the button has setting in itself and responds to external events also itself. In fact, it contains its own model (the so-called model of a View – View Model) and its own Controller. Therefore, the current programmes are more like a complex hierarchy of the View, the Controllers and the Model.

Disadvantages of MVC

One disadvantage is the strong interconnectivity of the components. This makes Unit testing complicated. And taking into account the complex hierarchy of the Controllers, the View and the View Model in modern programmes, which were conceived as applications based on MVC, Unit testing is practically impossible.

Another problem is the “thickening” of a business model. Why is this happening? The View can have complex states. For example, a difficult logic of text input field validation, and its light color depending on the outcome of the validation transaction. This state can not be directly saved in the input field of a View Model (moreover, it can not be configured in a graphic editor IDE).

  • Where to “transfer” this state then?
  • What about the Model and the Controller?

The Controller, in a classic MVC, should not save the state of a View (i.e. ViewModel), thus these complex states need to be implemented in the Model. So, in addition to the Domain Model, the Model also includes a part of the text inputs ViewModel.

Apple MVC

One of the attempts to adapt the classic MVC – Apple MVC, on which, actually, the frameworks Cocoa and CocoaTouch are built. In this pattern the Model is the same as in the classic MVC. It must be active (i.e. informs about changes in its condition, usually with the help of a pattern Observer.

In Cocoa and CocoaTouch frameworks for these purposes NSNotificationCenter and KVO can be used) autonomously, and don’t have to know about other components. View is also similar to the View from MVC (can be a hierarchy of components). With the aim of reducing class interconnectivity, the View does not have the direct access to the Model.

Apple MVC scheme

A user performs some action on the View. The View handles some of the events (View logic), and forwards the others to the Controller. The Controller makes a decision to process the event, and, if necessary, changes the state of a Model. If the state of the Model is changed, the Controller will be notified, and again it has to decide how to handle these changes. The Controller reads the new values from the Model, if necessary performs some transformations with them (to prepare them for display) and sets new values for the View.

Advantages over the classic MVC

In this pattern, there is no longer a connection between the View and the Model. Also the View’s state and the processing logic of representation are in the Controller. This segregation of duties is more appropriate in the current circumstances.

The disadvantage of this pattern is that the Controller contains some part of the View’s state and almost all the View logic. And as the Controller also serves as a mediator between the View and the Model, it becomes a very attractive place for Application logic accommodation. Therefore, in practice the UIViweController classes become too bulky. Very often, because of the strong relationship between the Controller and the view, they are regarded as components of the presentation layer.

iOS architectural patterns

Above, such concepts as “View Logic” and “Presentation Logic” were encountered. What are these? View Logic – a logic associated with the management of widgets hierarchy, animated transitions from one scene to another, showing a dialogue, etc.

Presentation Logic – logic associated with the transformation of domain model to a model that can be displayed on the View, and processing the events from the View that require manipulation of the domain model. Domain Logic – a fundamental logic that runs at the level of  the Model with Model objects. Domain logic thus can be reused in another application.

Application logic – a logic inherent in a particular application. This one is different from the Domain logic – it can not be reused, because it is specific and unique to a particular application.

MVP

MVP (Model View Presenter) is a further development of the MVC pattern. The Controller is replaced by the Presenter. Presenter, unlike Controller in the classic MVC:

  • holds the state of the View.
  • changes the state of the View.
  • handles events of the View.
  • transforms a Domain Model into a ViewModel.

Otherwise, Presenter is similar to the Controller from the classic MVC:

  • owns the Model.
  • changes the state of the Model (through calling appropriate methods) in response to external stimuli.
  • may contain Application Logic.

MVP was born in the early 90’s of the last century at IBM. As in the case of MVC, due to different interpretations of its pattern, several versions of it appeared. Martin Fowler defined these variations of MVP:

  • Presentation Model.
  • Supervision Controller.
  • Passive View.

They are all similar but differ mostly according to the connection between the View and the Presenter, and the update sequence of the View. A hierarchy of widgets usually plays the role of the View. The Model in MVP is not different from the model in MVC.

Supervision Controller

The closest pattern to MVC. The interaction of the components is shown in the scheme below.

Interaction of components in Supervision Controller

Supervision Controller View:

  • implements the view logic.
  • forwards events to the Presenter.
  • as in the classic MVC, observes the Model (with the help of Data Binding or implements the Observer pattern).
  • does not change the state of a Model directly.
  • may request data from the Presenter or read the Model, when it becomes a must.

While Presenter handles events of the View and changes the state of a Model (through calling appropriate methods). Unlike the Controller in a classic MVC, the Presenter keeps and changes the state of the View, if it is not possible to do with the help of a connection between the Model and the View through Data Binding or Observer (we are talking about a complex state).

Benefits of the Supervision Controller are that the state of View is located in the Presenter now (not in the Model). Presenter deals with the Presentation logic, therefore the View and the Model become “thinner”. The downside is that the View knows about the Model and the Presenter, and thus is dependent on them. This greatly complicates Unit testing.

Presentation Model

Further development of MVP where there is no Supervision Controller’s disadvantage – the connection between the View and the Model. The scheme of interaction between the components is:

ios mvp

View:

  • is responsible for the view logic;
  • redirects all events to the Presenter;

Unlike the classic MVC and Supervision Controller, the View doesn’t have a direct access to the Model. It does not know about it.

Presenter:

  • moves the state of the View into a separate Presentation Model which is a part of the Presenter;
  • interacts and provides an interface to the Domain Model (i.e. is a facade for the View);
  • observes the change of  the Model’s state;
  • provides a public interface which the View uses for the interaction with the Presenter.

The scheme works as follows:

There is an event in the View. The View can try to handle it itself, and request data from the Presenter. If the View can not handle the event, it delegates this event to the Presenter, which decides how to process it. If necessary, the Presenter changes the state of the Model. The Model informs the Presenter about the change of its state. The Presenter reads the new values of the Model, if necessary, performs additional logic on them and updates the View.

The advantage of this model over Supervision Controller is that the View has no connection with the Model, which facilitates the Unit testing. Disadvantages include the need to create additional interfaces (at least for the View and the Presenter) and the logic of updating in the very View, which doesn’t greatly simplify the testing.

Humble View

The difference between Humble View and Presentation model is in the View and the way how its state is updating. The View becomes passive. Previous variations of MVP didn’t impose restrictions on the View and it could ask the Presenter for some data. The passive View is limited in this case, it no longer asks the Presenter for any data.

Any changes in the state of the View are performed from the Presenter. The View doesn’t know about the Presenter’s or  Model’s existence. The View’s passivity simplifies the Unit testing at most. As in the case of every architectural pattern, there are many issues about the relation of the components. The most common ones:

  • Who owns the View and Presenter in MVP?

The View usually has a strong reference to the Presenter. In its turn, the Presenter has a strong reference to the Model and a weak one to the View. The Model, as in the classic MVC, knows nothing about the View and the Presenter.

  • Who creates the View and  the Presenter?

It is believed that a View is created by a Presenter. However, the Presenter requires a Model, i.e. the View, creating the Presenter, has to configure it by a model, and here follows that she knows about the existence of the model. This sequence does not suit us, because we are trying to get minimal connectivity between components (for an easier testing and a greater flexibility).

So it will be better if the next View Presenter is created by another Presenter or in a separate Router class (which may also be engaged in configuration and creation of the next View). However, there are no clear rules.

MVP for iOS

After some theory, we can proceed to a real development. A typical iOS application is being built around a central UIViewController class, which has many responsibilities and so is the most attractive place to put UI logic and a part of the application logic. However, we mentioned above that due to a strong cohesion between the View and the Controller (in the context of iOS UIViewController and UIView) it is convenient to think about them as a View.

MVP adapted for iOS

Let’s consider, for example, a simple application which consists of two scenes. It allows you to load random photos of cats from the Internet (Load Cat Scene) using the REST service http://random.cat/meow, apply built-in photo filters on the cat’s picture, and save the edited photo (Edit Cat Scene).

You can download sample application there: https://github.com/thinkmobiles/CatApp_MVP_Sample

Load Cat Scene View displays the activity indicator when the photo is loaded, actual loaded photo and the URL of the picture. The Presenter will interact with Load Cat Scene View with the help of “minimal” interface LoadCatViewProtocol.


protocol LoadCatViewProtocol: View {
    
    func updateLoadingState(_ loadingState: Bool)
    func updateTitle(_ imageTitle: String?)
    func updateImage(_ image: Data?)
}
 
class LoadCatPresenter: LoadCatPresenterProtocol {
    
    weak var view: LoadCatViewProtocol!
    private var isLoading: Bool
    private var image: Data?
    private var imageTitle: String?
    
    func installView(_ view: View) {
        self.view = view as! LoadCatViewProtocol
    }
 
    func updateUI() {
        view.updateLoadingState(isLoading)
        view.updateTitle(imageTitle)
        view.updateImage(image)
    }
 
   func load() {
        
        guard !isLoading else { return }
        
        isLoading = true
        image = nil
        imageTitle = nil
        
        updateUI()
        loadCat()
    }
. . . 
}

Load Cat Scene allows you to start loading and cancel it, and also to go to the next scene for image editing. These events are initiated by the user and the View just redirects them to the Presenter, calling its methods. The View interacts with the Presenter through the protocol LoadCatPresenterProtocol.


protocol LoadCatPresenterProtocol: Presenter {
    
    func load()
    func cancel()
    func updateUI()
    func edit()
    
    var catProvider: CatProvider! { get set }
}
 
class LoadCatViewController: UIViewController, LoadCatViewProtocol {
    
    var presenter: LoadCatPresenterProtocol!
 
    @IBAction func actLoad(_ sender: UIBarButtonItem) {
        presenter.load()
    }
    
    @IBAction func actCancel(_ sender: UIBarButtonItem) {
        presenter.cancel()
    }
 
	func setPresenter(_ presenter: Presenter) {
        self.presenter = presenter as! LoadCatPresenterProtocol
    }
. . . 
}

In our test project the transition to the next scene is made without the Router class.

  • To switch from Load Cat Scene to Edit Cat Scene you need to press Edit.
  • LoadCatViewController redirects this event into LoadCatPresenter.
  • LoadCatViewController doesn’t know that this event initiates the transition.
  • LoadCatPresenter creates EditCatPresenter and configures it with necessary models.
  • To display the next scene LoadCatPresenter calls a method of LoadCatViewController showEditScene and pass EditCatPresenter there.
  • LoadCatViewController creates the next View, connects it with a received Presenter and displays.

protocol LoadCatViewProtocol: View {

    func showEditScene(withPresenter presenter: Presenter)
}
 
class LoadCatViewController: UIViewController, LoadCatViewProtocol {
	
	@IBAction func actEdit(_ sender: UIBarButtonItem) {
        loadButton.isEnabled = false
        editCat()
    }
 
	func showEditScene(withPresenter presenter: Presenter) {
        let nextViewController = storyboard!.instantiateViewController(withIdentifier: Constants.editCatViewControllerStoryboardId) as! View
        presenter.installView(nextViewController)
        nextViewController.setPresenter(presenter)
        present(nextViewController as! UIViewController, animated: true, completion: nil)
    }
. . .
}

If it is the first scene, it is convenient to perform this configuration by all principles of Apple in UIApplicationDelegate.


class AppDelegate: UIResponder, UIApplicationDelegate {
 
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
 
        let view = window?.rootViewController as! LoadCatViewProtocol
        let presenter = LoadCatPresenter()
        presenter.catProvider = catProvider
        presenter.installView(view)
        view.setPresenter(presenter)
        
        return true
    }    
}

MVVM

Despite all the advantages of MVP, with the IDE development and frameworks it didn’t fit in the automated application development, because it required “manual” work. The next pattern should solve these problems. MVVM (Model View ViewModel) was developed by engineers from Microsoft Ken Cooper and Ted Peters and announced by John Gossman in his blog in 2005.

The purpose of the pattern is separation between the the user interface from development and business logic development, and facilitating the application testing using the main features of WPF and Silverlight platforms. Although the pattern of specialization was conceived for Microsoft technology, it can be used in Cocoa / CocoaTouch framework.

The Structure of  MVVM

MVVM is derived from MVC pattern and consists of the 3 following components: Model, View, ViewModel. Model is different from the Model in MVP and MVC:

  • it is a domain model;
  • includes  data, business logic and validation logic;
  • does not depend on other components (View and ViewModel).

View :

  • Determines the structure, location and appearance of the user interface (as MVP, Apple MVC).
  • Has View’s logic (View’s logic): animations, transitions between the View and manipulations with the child Views
  • Keeps a strong reference to the ViewModel, but knows nothing about the Model.
  • Monitors the ViewModel and communicates with it using the Data Binding or referring to it directly.

To avoid the strong relationship between the View and the ViewModel you need to create an interface through which the View will communicate with  the ViewModel. The ViewModel is the mediator between the View and the Model and is responsible for the processing of presentation logic.

ViewModel:

  • keeps the state of the View;
  • knows about the Model and can change its state (calling methods of appropriate classes);
  • transforms the data from the Model into a format which is more convenient for the View;
  • validation of data which come from the View;
  • doesn’t know about the View and can interact with View only through the Data Binding mechanism;

In Cocoa there is its own Data Binding mechanism, but, in CocoaTouch, there isn’t. We could do only with KVO, but this thing is not convenient to use and allows you to implement only a unilateral binding. Data Binding, in its turn, makes it possible to implement the full potential inherent in MVVM and facilitate the development in general. So you should use some third-party libraries that provide Data Binding to CocoaTouch, or reactive programming.

UIViewController in a MVVM and MVP is regarded as a part of View.

MVVM Structure in Cocoa / CocoaTouch

An important question arises during the transition from Apple’s MVC to MVVM: how to implement navigation? As mentioned above, the View directly performs transition to a different View. So there are two options for how to make the transition:

  • The easiest one is when the transition is initiated from a View. In this case, the ViewModel of a current scene creates the ViewModel of the next scene (if necessary it configures it by a Model). Then the View creates the View of the next scene, passes it the new ViewModel, and performs the transition.
  • The transition is initiated from the ViewModel. As the ViewModel knows nothing about the View, it cannot make the transition. In this case, a special component- Router – is required, it knows the hierarchy of the View and how to make the transition. The ViewModel can pass to the Router a ViewModel or Model of the next scene. The Router deals with everything else.

So, MVVM and MVP (Humble View) differ mostly in a Presentation layer (in MVP it is presented by a Presenter and in the MVVM by a ViewModel). The advantage of the MVVM over MVP (Humble View) is that the Presentation layer is completely independent of the View (which means much easier testing) and DataBinding usage. Together it becomes a more attractive candidate for use in modern IDE, and reduces the amount of code for synchronizing the View with the ViewModel).

The disadvantage of the MVVM is mostly in DataBinding mechanisms, as in certain situations, it may require significant memory resources, and also is a weak spot for the Memory Leak emergence. Next, we will consider the example of the application described in the previous section, but using MVVM pattern. You can download the sample app there: https://github.com/thinkmobiles/CatApp_MVVM_Sample

The user interface of the application and of a Model (Cat, CatProvider) are identical. They differ only in Presentation logic, which will be in the main focus. The View components are presented by LoadCatViewController and EditCatViewController. LoadCatViewController interacts with LoadCatViewModel via interface:


protocol CatViewModelProtocol {
 
    var isLoading: Observable { get }
    var isEditable: Observable { get }
    var title: Observable<String?> { get }
    var imageData: Observable<Data?> { get }
 
    var editCatViewModel: EditCatViewModelProtocol? { get }
 
    func loadNextCat()
    func cancelCurrentDownloading()
}

LoadCatViewModel contains a set of features that define the state of a  LoadCatViewController and a set of methods that correspond to actions that a user can make. For Data Binding mechanism we used BondAs Load Cat is the initial scene, it is obvious that its configuration is performed in AppDelegate:


final class AppDelegate: UIResponder, UIApplicationDelegate {
 
    var window: UIWindow?
 
    lazy var catProvider = CatProvider()
 
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let catViewModel = CatViewModel(catProvider: catProvider)
        (window?.rootViewController as? CatViewController)?.viewModel = catViewModel
        return true
    }
}

The configuration Edit Cat scene, as in the case of MVP, takes place separately in the View and the ViewModel.


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let editCatViewController = segue.destination as? EditCatViewController {
            editCatViewController.viewModel = viewModel.editCatViewModel
        }
    }
  • First, LoadCatViewController, using Segue mechanism, creates EditCatViewController.
  • Then, in the method prepareForSegue, LoadCatViewController asks LoadCatViewModel the configured ViewModel for the next scene, i.e. EditCatViewModel which contains a current Cat Model.
  • Further, we pass this EditCatViewModel into EditCatViewController.
  • Unit testing is the application testing in the ViewModel and  the Model. In the test project, you will find examples of Unit tests.

VIPER

The examined above architectural patterns have one disadvantage. If you try to divide the architecture into layers, it is likely you will have difficulty with the Presenter or with the View Model. Which layer do they belong to? This question has no clear answer: you can introduce a separate Presentation layer for the Presenter, or it can belong to the Application Logic. The same with MVVM. This ambiguity creates another problem.

It is very difficult to separate the Application Logic from the Domain Model Logic. So often there’s no separation and are in the same layer. Besides, the presence of an Application Logic in a Presenter sometimes makes it difficult to test different  Use Cases. Another problem in previous architectures is assembly and navigation. In large projects for several dozens of scenes it is obvious that this is a responsibility  of a separate module Router.

In 2012  a remarkable article was published. The Clean Architecture and several speeches on the subject. Later, in the Mutual Mobile we’ve adapted a little for iOS, and a new pattern VIPER enters. It is the acronym of View, Interactor, Presenter, Entity, Router – basic components that make up the application. See how they interact below.

Structure of a VIPER Project

View

As with MVP (Passive View), it is a visualization of the data that come from the Presenter. The View communicates with the Presenter through a protocol at a higher level than the level of UI classes. The Presenter is not aware of specific classes that make up a hierarchy of the View. To share data between the View and the Presenter it is convenient to use separate structures (i.e. classes that have no methods that could change its state). Only the View and the Presenter know about these classes.

Presenter. Functions as in MVP, with the difference that it should not contain Application Logic. We mainly engage the Presenter in the transformation of the data.

Interactor. These objects encapsulate a separate Use Case (we will call this logic Application Logic) of an application. The Interactor works with the Presenter and with the Model. The Interactor never passes object classes which belong to the Model Layer to the Presenter. Thus the Presenter does not depend on the Model. Moreover, he does not know about the Model’s existence.

Model. The same as in the previous patterns. With the model of direction only the Interactor works. The Model is unaware of the existence of other components. The Model layer may contain various managers (to create or preserve Entity) and objects that encapsulate data processing algorithms.

Entity. The entity is PONSO (Plain Old NSObject) objects that contain only data and do not include methods of their processing (e.g. all their properties are readonly, and objects of classes NSManagerObject can not leave the boundary of a Model layer).

Routing. Wireframe and Presenter have the responsibilities for the navigation in VIPER.

The Presenter receives the View’s events and knows how to respond to them. But the Presenter knows nothing about the hierarchy of the View and contains the View Logic (animated switching between scenes – an example View Logic) and can not switch between scenes.

Here, it will need Wireframe – an object that contains a reference to UIWindow, can create the View / UIViewController and knows how to put them in the View hierarchy. It also follows that Wireframe is the ideal location for such transactions as a custom transition between scenes. As an example, let’s consider the VIPER version of a test project, which was described above for the MVP.

You can download sample code there: https://github.com/thinkmobiles/CatApp_VIPER_Sample

Compare protocols for LoadCatView in MVP and VIPER version of the project.

MVP VIPER
protocol LoadCatViewProtocol: View {

   func updateLoadingState(_ loadingState: Bool)

   func updateTitle(_ imageTitle: String?)

   func updateImage(_ image: Data?)

   func showEditScene(withPresenter presenter: Presenter)

   func finishedEdit()

}

protocol LoadCatViewProtocol: View {

   func updateLoadingState(_ loadingState: Bool)

   func updateTitle(_ title: String?)

   func updateImage(_ image: UIImage?)

   func finishEditing()

}

They differ only in a method


   func showEditScene(withPresenter presenter: Presenter)

because in terms of VIPER new scenes or dialogues display is the duty of the Wireframe. Accordingly, LoadCatViewProtocol implementation of both projects is almost identical. Compare LoadCatPresenter for MVP and VIPER projects.

MVP VIPER
protocol LoadCatPresenterProtocol: Presenter {

   func load()

   func cancel()

   func updateUI()

   func edit()

   ar catProvider: CatProvider! { get set }

}

protocol LoadCatPresenterProtocol: Presenter {

   func load()

   func cancel()

   func updateUI()

   func edit()

   var loadCatInteractor: LocadCatInteractor! { get set }

}

The difference between them is not great. MVP version of the project contains a variable catProvider which refers to the Model layer. In the VIPER version of a project  the Presenter doesn’t have to be dependent on the Model layer.

Since loading of a picture by pressing the button is a User Case (or Application Logic), to implement the functionality an  Interactor is required (variable loadCatInteractor). In general, the Interactor has an Input (an interface through which the Presenter can interact with it).


protocol LoadCatInteractorInput {
    func loadCat()
    func cancelLoad()
}

and Output through which interacts with the Presenter


protocol LoadCatInteractorOutput {
    func didLoadCatURL(_ catURL: NSURL?, success: Bool, cancelled: Bool)
    func didLoadCatImage(_ image: Data?, success: Bool, cancelled: Bool)
}

Therefore, the loading of a cat picture process by pressing the button looks like this


class LoadCatViewController: UIViewController, LoadCatViewProtocol {
    
    var presenter: LoadCatPresenterProtocol!
    
    @IBAction func actLoad(_ sender: UIButton) {
        presenter.load()
    }
. . . 
}
 
class LoadCatPresenter: LoadCatPresenterProtocol, LoadCatInteractorOutput {
 
    var loadCatInteractor: LocadCatInteractor!
     
    public func load() {
        
        // Some code to prepare UI
        loadCatInteractor.loadCat()
    }
    
    //MARK: LoadCatInteractorOutput
    
    func didLoadCatURL(_ catURL: NSURL?, success: Bool, cancelled: Bool) {
        // Show the URL
    }
    
    func didLoadCatImage(_ image: Data?, success: Bool, cancelled: Bool) {
        // Show the image
    }
    . . . 
}

The interaction between the Interactor and a  Model layer. The Model layer is presented by CatProvider and Cat classes. Since it is pretty primitive, then for data exchange between the Interactor and the Model we did not create Entity classes. 

Let’s consider switching between scenes. As we’ve mentioned above, in the VIPER project it is the responsibility of the Wireframe. If the next scene needs some data from the previous one, they can pass them through the Wireframe.


class LoadCatPresenter: LoadCatPresenterProtocol, EditCatPresenterDelegate {
 
    func edit() {
        let image = UIImage(data: self.image!)
        let editCatPresenter = EditCatPresenter()
        editCatPresenter.delegate = self
        editCatPresenter.image = image!
        
        view.showEditScene(withPresenter: editCatPresenter)
    }
. . .
}

Thus, is not convenient to use Seguey mechanism for transitions between scenes any more, but it is not a reason to refuse such a convenient mechanism for work with scenes as UIStoryboard. The scenes will just be without Seguey here.

An ordinary VIPER project consists of many modules you need to configure. For our simple example, it is enough to do it at app launch in a separate Dependencies class. However, in complex projects, it is easier to use other solutions or libraries.

Testing. The testing of a VIPER project is similar to the MVP, with the difference that the Application Logic is delivered into separate classes – Interactors. On one hand, you have to write more code for Unit testing, on the other hand, simpler algorithms for individual functional testing (User Cases). In our test project, you will find examples of Unit tests for all VIPER project.

Conclusion

In this article, we examined the evolution of architectural patterns that can be used to develop iOS applications. Each pattern in the chain of evolution improves the previous one. The boundaries between the components and their responsibilities are clarified  (if necessary,  new layers or components are introduced), which facilitates the development and support.

Get free estimation for your mobile app

Post your project or request a dedicated team - we'll quickly match you with the right experts.

Post project

16 sharings