iOS – Why Are Architecture Patterns Important In iOS Application ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Why are Architecture Patterns important in iOS application) .

In this article, we will learn about Architecture Patterns importance in iOS application. Every iOS developer is familiar with issues related to product testing, code refactoring and support via ViewController. We also know the latter as a Massive View Controller. In search for solutions, we’ve delved into a profound investigation of programming patterns for iOS. At some point we 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. So we can ask some questions from ourself like these :

  • Who supposed to own networking request: a Model or a Controller?
  • How do I pass a Model into a View Model of a new View?
  • Who creates a new VIPER module: Router or Presenter?

In this article, we cover the following iOS architecture patterns which is used in iOS Application development :

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

A famous quote about learning is :

” That is what learning is. You suddenly understand something you’ve understood all your life, but in a new way.”

So Let’s begin.


Why care about choosing the architecture?

Because if we don’t, one day, debugging a huge class with dozens of different things, we’ll find ourself being unable to find and fix any bugs in our class. Naturally, it is hard to keep this class in mind as complete entity, thus, we’ll always be missing some important details. If we are already in this situation with our application, it is likely that:

  • This class is the UIViewController subclass.
  • Our data stored directly in the UIViewController
  • Our UIViews do almost nothing
  • The Model is a dumb data structure
  • Our Unit Tests cover nothing

And this can happen, even despite the fact that we are following Apple’s guidelines and implementing Apple’s MVC pattern, so don’t feel bad. There is something wrong with the Apple’s MVC, but we’ll get back to it later.

Let’s define features of a good architecture:

  1. Balanced distribution of responsibilities among entities with strict roles. The easiest way to defeat complexity is to divide responsibility among multiple entities following the single responsibility principle.
  2. Testability usually comes from the first feature (and don’t worry: it is easy with appropriate architecture).
  3. Ease of use and a low maintenance cost. The least code we have to use, the fewer bugs we have to worry about.


Why is Distribution required?

Distribution keeps a fair load on our brain while we are trying to figure out how things work. If we think the more we develop, the better our brain will adapt to understanding complexity, then we are right. But this ability doesn’t scale linearly and reaches the cap quickly. So the easiest way to defeat complexity is to divide responsibilities among multiple entities following the Single Responsibility Principle.


Why is Testability required?

This is usually not a question for those who already felt gratitude to unit tests, which failed after adding new features or because of refactoring some intricacies of the class. This means the tests saved those developers from finding issues in runtime, which might happen when an app is on a user’s device and the fix takes a week to reach the user.


Why Ease of use?

This does not require an answer, but it is worth mentioning that the best code is the code that has never been written. Therefore, the less code we have, the fewer bugs we have. This means that desire to write less code should never be explained solely by laziness of a developer, and we should not favour a smarter solution closing our eyes to its maintenance cost.


Essential Aspects of Application Architecture

There are five essential aspects of Application Architecture as below :

  • Separation of concern
  • State sharing
  • State propagation
  • View controller communication
  • Parallelism


Aim of Application Architecture

The purpose of Application Architecture is :

  • Fix the massive-view-controller issue
  • Increase testability
  • Improve maintainability
  • Scale with team size


iOS Architecture Patterns

Nowadays we have many options when it comes to architecture design patterns:

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

First three of them assume putting the entities of the app into one of 3 categories:

  • Models — responsible for the domain data or a data access layer which manipulates the data, think of ‘Person’ or ‘PersonDataProvider’ classes.
  • Views — responsible for the presentation layer (GUI), for iOS environment think of everything starting with ‘UI’ prefix.
  • Controller/Presenter/ViewModel — the glue or the mediator between the Model and the View responsible for altering the Model by reacting to the user’s actions performed on the View and updating the View with changes from the Model.

Having entities divided allows us to:

  • Understand them better (as we already know)
  • Reuse them (mostly applicable to the View and the Model)
  • Test them independently

So Let’s start with one by one patterns


1. Classic MVC

Here, the View is stateless. It is simply rendered by the Controller once the Model is changed. Think of the web page completely reloaded once. We press on the link to navigate somewhere else. Although it is possible to implement the traditional MVC in iOS application, it doesn’t make much sense because of the architectural problem — it tightly couples all three entities, each entity knows about the other two. This dramatically reduces reusability of each of them — that is not what you want to have in your application. For this reason, we skip even trying to write a canonical MVC example.

Traditional MVC doesn’t seem to be applicable to modern iOS development.


2. Apple’s MVC


Expection

The Controller is a mediator between the View and the Model so that they don’t know about each other. The least reusable is the Controller and this is usually fine for us, since we must have a place for all that tricky business logic that doesn’t fit into the Model.

In theory, it looks very straightforward, but we feel that something is wrong, right? We even heard people abbreviating MVC as the Massive View Controller. View controller offloading became an important topic for the iOS developers. Why does this happen if Apple just took the traditional MVC and improved it a bit?


Reality

Cocoa MVC encourages us to write Massive View Controllers, because they are so involved in View’s life cycle that it’s hard to say they are separate. Although we still have ability to offload some business logic and data transformation to the Model, we have a little choice for offloading work to the View, at most of the times all the responsibility of the View is to send actions to the Controller. The view controller ends up being a delegate and a data source of everything, and is usually responsible for dispatching and cancelling the network requests and… we name it.

How many times have we seen code like this:

var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(user)

The cell which is the View configured directly with the Model, so MVC guidelines are violated, but this happens all the time, and usually people don’t feel it is wrong. If we strictly follow the MVC, then we supposed to configure the cell from the controller, and don’t pass the Model into the View, and this will increase the size of our Controller even more.

Cocoa MVC is reasonably unabbreviated as the Massive View Controller.

The problem might not be clear until it comes to the Unit Testing (hopefully, it does in our project). Since our view controller is tightly coupled with the view, it becomes difficult to test because we have to be very creative in mocking views and their life cycle, while writing the view controller’s code in such a way, that our business logic is separated as much as possible from the view layout code.

Let’s have a look on the simple playground example:

/**
 * MVC iOS Design Pattern
 *
 */

import UIKit
import PlaygroundSupport

struct Person { // Model
    let firstName: String
    let lastName: String
}

class GreetingViewController : UIViewController {   // View + Controller
    var person: Person!
    var showGreetingButton: UIButton!
    var greetingLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
        
        self.setupUIElements()
        self.layout()
    }
    
    func setupUIElements() {
        self.title = "Test"
        
        self._setupButton()
        self._setupLabel()
    }
    
    private func _setupButton() {
        self.showGreetingButton = UIButton()
        self.showGreetingButton.setTitle("Click me", for: .normal)
        self.showGreetingButton.setTitle("You badass", for: .highlighted)
        self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)
        self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)
        self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = false
        self.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)
        self.view.addSubview(self.showGreetingButton)
    }
    
    private func _setupLabel() {
        self.greetingLabel = UILabel()
        self.greetingLabel.textColor = UIColor.white
        self.greetingLabel.textAlignment = .center
        self.greetingLabel.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(self.greetingLabel)
    }
    
    func layout() {
        self._layoutButton()
        self._layoutLabel()
        
        self.view.layoutIfNeeded()
    }
    
    private func _layoutButton() {
        // layout button at the center of the screen
        let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)
        
        self.view.addConstraints([cs1, cs2])
    }
    
    private func _layoutLabel() {
        // layout label at the center, bottom of the screen
        let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)
        let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)
        
        self.view.addConstraints([cs1, cs2, cs3])
    }
    
    @objc func didTapButton(sender: UIButton) {
        self.greetingLabel.text = "Hello " + self.person.firstName + " " + self.person.lastName
    }
}

let model = Person(firstName: "Wasin", lastName: "Thonkaew")
let vc = GreetingViewController()
vc.person = model

PlaygroundPage.current.liveView = vc.view

MVC assembling can be performed in the presenting view controller.

This doesn’t seem very testable, right? We can move generation of greeting into the new GreetingModel class and test it separately, but we can’t test any presentation logic (although there is not much of such logic in the example above) inside the GreetingViewController without calling the UIView related methods directly (viewDidLoad, didTapButton) which might cause loading all views, and this is bad for the unit testing.

In fact, loading and testing UIViews on one simulator doesn’t guarantee that it would work fine on the other devices (e.g. iPad), so we’d recommend removing “Host Application” from our Unit Test target configuration and run our tests without our application running on simulator.

The interactions between the View and the Controller aren’t really testable with Unit Tests.

With all that said, it might seems that Cocoa MVC is a pretty bad pattern to choose. But let’s assess it in terms of features defined in the beginning of the article:

  • Distribution —the View and the Model in fact separated, but it tightly couples the View and the Controller.
  • Testability — because of the bad distribution, we’ll probably only test our Model.
  • Ease of use — the least amount of code among other patterns. In addition, everyone is familiar with it, thus, it’s easily maintained even by the unexperienced developers.

Cocoa MVC is the pattern of our choice if we are not ready to invest more time in our architecture, and we feel that something with higher maintenance cost is an overkill for our tiny pet project.

Cocoa MVC is the best architectural pattern in terms of the speed of the development.


3. 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 like 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 – Cocoa MVC’s promises delivered (Passive View Variant)

This diagram doesn’t it look exactly like the Apple’s MVC? Yes, it does, and its name is MVP (Passive View variant). Here, the MVP’s mediator (Presenter) has nothing to do with the life cycle of the view controller, and the View can be mocked easily, so there is no layout code in the Presenter at all, but it updates the View with data and state. 

In terms of the MVP, the UIViewController subclasses are in fact the Views and not the Presenters. This distinction provides superb testability, which comes at a cost of the development speed, because we have to make manual data and event binding, as we can see from the example:

/**
 * MVP iOS Design Pattern
 *
 */

import UIKit
import PlaygroundSupport

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

protocol GreetingViewPresenter {
    init(view: GreetingView, person: Person)
    func showGreeting()
}

class GreetingPresenter : GreetingViewPresenter {
    weak var view: GreetingView?
    let person: Person
    
    required init(view: GreetingView, person: Person) {
        self.view = view
        self.person = person
    }
    
    func showGreeting() {
        let greeting = "Hello " + self.person.firstName + " " + self.person.lastName
        self.view?.setGreeting(greeting: greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var presenter: GreetingViewPresenter!
    var showGreetingButton: UIButton!
    var greetingLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
        
        self.setupUIElements()
        self.layout()
    }
    
    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }
    
    func setupUIElements() {
        self.title = "Test"
        
        self._setupButton()
        self._setupLabel()
    }
    
    private func _setupButton() {
        self.showGreetingButton = UIButton()
        self.showGreetingButton.setTitle("Click me", for: .normal)
        self.showGreetingButton.setTitle("You badass", for: .highlighted)
        self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)
        self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)
        self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = false
        self.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)
        self.view.addSubview(self.showGreetingButton)
    }
    
    private func _setupLabel() {
        self.greetingLabel = UILabel()
        self.greetingLabel.textColor = UIColor.white
        self.greetingLabel.textAlignment = .center
        self.greetingLabel.translatesAutoresizingMaskIntoConstraints = false
        
        self.view.addSubview(self.greetingLabel)
    }
    
    func layout() {
        self._layoutButton()
        self._layoutLabel()
        
        self.view.layoutIfNeeded()
    }
    
    private func _layoutButton() {
        // layout button at the center of the screen
        let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)
        
        self.view.addConstraints([cs1, cs2])
    }
    
    private func _layoutLabel() {
        // layout label at the center, bottom of the screen
        let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)
        let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)
        
        self.view.addConstraints([cs1, cs2, cs3])
    }
    
    @objc func didTapButton(sender: UIButton) {
        self.presenter.showGreeting()
    }
}

// Assembling of MVP
// Note: Very important that these following lines will be within View when actually creating normal XCode project and follow design here.
let model = Person(firstName: "Wasin", lastName: "Thonkaew")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter

PlaygroundPage.current.liveView = view.view

The MVP is the first pattern that reveals the assembly problem, which happens because of having three separate layers. Since we don’t want the View to know about the Model, it is not right to perform assembly in presenting view controller (which is the View), thus we have to do it somewhere else. For example, we can make the app-wide Router service, which will perform assembly and the View-to-View presentation. This issue arises and has to be addressed not only in the MVP but also in all the following patterns.

Let’s look on the features of the MVP:

  • Distribution — we have the most of the responsibilities divided between the Presenter and the Model, with the pretty dumb View (in the example above the Model is dumb as well).
  • Testability — is excellent, we can test most of the business logic because of the dumb View.
  • Easy of use — in our unrealistically simple example, the amount of code is doubled compared to the MVC, but, idea of the MVP is very clear.

MVP in iOS means superb testability and a lot of code.


MVP – With Bindings and Hooters (Supervising Presenter Variant)

There is the other flavour of the MVP — the Supervising Controller MVP. This variant includes direct binding of the View and the Model while the Presenter (The Supervising Controller) still handles actions from the View and is capable of changing the View.

But as we have already learned before, vague responsibility separation is bad, and tight coupling of the View and the Model. That is like how things work in Cocoa desktop development.

Same as with the traditional MVC, we don’t see a point in writing an example for the flawed architecture.


4. 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 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.

In theory, the Model-View-ViewModel looks very good. The View and the Model are already familiar to us, but also the Mediator, represented as the View Model.

It is pretty similar to the MVP:

  • The MVVM treats the view controller as the View
  • There is no tight coupling between the View and the Model

In addition, it does binding like the Supervising version of the MVP; however, this time not between the View and the Model, but between the View and the View Model.

So what is the View Model in the iOS reality? It is basically UIKit independent representation of our View and its state. The View Model invokes changes in the Model and updates itself with the updated Model, and since we have a binding between the View and the View Model, the first is updated accordingly.


Bindings

Bindings come out of a box for the OS X development, but we don’t have them in the iOS toolbox. Of course we have the KVO and notifications, but they aren’t as convenient as bindings.

So, provided we don’t want to write them ourselves, we have two options:

  • One of the KVO based binding libraries like the RZDataBinding or the SwiftBond
  • The full scale functional reactive programming beasts like ReactiveCocoaRxSwift or PromiseKit.

In fact, nowadays, if we hear “MVVM” — we think ReactiveCocoa, and vice versa. Although it is possible to build the MVVM with the simple bindings, ReactiveCocoa (or siblings) will allow us to get most of the MVVM.

There is one bitter truth about reactive frameworks: the great power comes with the great responsibility. It’s really easy to mess up things when we go reactive. In other words, if we do something wrong, we might spend a lot of time debugging the app, so just take a look at this call stack.

In our simple example, the FRF framework or even the KVO is an overkill, instead we’ll explicitly ask the View Model to update using showGreeting method and use the simple property for greetingDidChange callback function.

/*** MVVM iOS Design Pattern**/ import UIKitimport PlaygroundSupportstruct Person { let firstName: String let lastName: String} protocol GreetingViewModelProtocol: class {var greeting: String? { get }var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set }init(person: Person)func showGreeting()}class GreetingViewModel : GreetingViewModelProtocol {let person: Personvar greeting: String? {didSet {self.greetingDidChange?(self)}}var greetingDidChange: ((GreetingViewModelProtocol) -> ())?required init(person: Person) {self.person = person}func showGreeting() {self.greeting = "Hello " + self.person.firstName + " " + self.person.lastName}}class GreetingViewController : UIViewController {var viewModel: GreetingViewModelProtocol? {didSet {self.viewModel?.greetingDidChange = { [unowned self] viewModel inself.greetingLabel.text = viewModel.greeting}}}var showGreetingButton: UIButton!var greetingLabel: UILabel!override func viewDidLoad() {super.viewDidLoad()self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)self.setupUIElements()self.layout()}func setupUIElements() {self.title = "Test"self._setupButton()self._setupLabel()}private func _setupButton() {self.showGreetingButton = UIButton()self.showGreetingButton.setTitle("Click me", for: .normal)self.showGreetingButton.setTitle("You badass", for: .highlighted)self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = falseself.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)self.view.addSubview(self.showGreetingButton)}private func _setupLabel() {self.greetingLabel = UILabel()self.greetingLabel.textColor = UIColor.whiteself.greetingLabel.textAlignment = .centerself.greetingLabel.translatesAutoresizingMaskIntoConstraints = falseself.view.addSubview(self.greetingLabel)}func layout() {self._layoutButton()self._layoutLabel()self.view.layoutIfNeeded()}private func _layoutButton() { let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)self.view.addConstraints([cs1, cs2])}private func _layoutLabel() { let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)self.view.addConstraints([cs1, cs2, cs3])}@objc func didTapButton(sender: UIButton) {guard let vm = self.viewModel else { return }vm.showGreeting()}} let model = Person(firstName: "Wasin", lastName: "Thonkaew")let view = GreetingViewController()let viewModel = GreetingViewModel(person: model)view.viewModel = viewModelPlaygroundPage.current.liveView = view.view 

And again back to our feature assessment:

  • Distribution — it is not clear in our tiny example, but, in fact, the MVVM’s View has more responsibilities than the MVP’s View. Because the first one updates its state from the View Model by setting up bindings, when the second one just forwards all events to the Presenter and doesn’t update itself.
  • Testability — the View Model knows nothing about the View, this allows us to test it easily. The View might be also tested, but since it is UIKit dependant we might want to skip it.
  • Easy of use — its has the same amount of code as the MVP in our example, but in the real app where we’d have to forward all events from the View to the Presenter and to update the View manually, MVVM would be much skinnier if we used bindings.

The MVVM is very attractive, since it combines benefits of the aforementioned approaches, and, in addition, it doesn’t require extra code for the View updates because of the bindings on the View side. Nevertheless, testability is still on a good level.


5. VIPER

The examined above architectural patterns have one disadvantage. If we try to divide the architecture into layers, it is likely we will have difficulty with the Presenter or with the View Model. Which layer do they belong to? This question has no simple answer: we 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, 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. This gives the LEGO building experience transferred into the iOS app design.

By now, we agree that the granularity in responsibilities is very good. VIPER makes another iteration on the idea of separating responsibilities, and this time we have five layers.

  • View — As with MVP (Passive View), it is a visualization of the data that comes 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.
  • Interactor — This layer contains business logic related to the data (Entities) or networking, like creating new instances of entities or fetching them from the server. For those purposes we’ll use some Services and Managers which are not considered as a part of VIPER module but an external dependency.
  • Presenter — This layer contains the UI related (but UIKit independent) business logic, invokes methods on the Interactor.
  • Entities — Our plain data objects, not the data access layer, because that is a responsibility of the Interactor.
  • Router — This layer is responsible for the segues between the VIPER modules. Wireframe and Presenter have the responsibilities for the navigation in VIPER.

Basically, VIPER module can be a one screen or the whole user story of our application — think of authentication, which can be one screen or several related ones. How small are our “LEGO” blocks supposed to be? — It’s up to us.

If we compare it with the other patterns, we’ll see a few differences of the distribution of responsibilities:

  • Model (data interaction) logic shifted into the Interactor with the Entities as dumb data structures.
  • Only the UI representation duties of the Controller/Presenter/ViewModel moved into the Presenter, but not the data altering capabilities.
  • VIPER is the first pattern which explicitly addresses navigation responsibility, which is supposed to be resolved by the Router.

Proper way of doing routing is a challenge for the iOS applications, the other patterns (MVC, MVP, MVVM) simply don’t address this issue.

The example doesn’t cover routing or interaction between modules, as those topics are not covered by the other patterns at all.

/*** VIPER iOS Design Pattern**/import UIKitimport PlaygroundSupportstruct Person { let firstName: Stringlet lastName: String}struct GreetingData {  let greeting: Stringlet subject: String}protocol GreetingProvider {func provideGreetingData()}protocol GreetingOutput: class {func receiveGreetingData(greetingData: GreetingData)}class GreetingInteractor: GreetingProvider {weak var output: GreetingOutput!func provideGreetingData() {let person = Person(firstName: "Wasin", lastName: "Thonkaew")let subject = person.firstName + " " + person.lastNamelet greeting = GreetingData(greeting: "Hello", subject: subject)self.output.receiveGreetingData(greetingData: greeting)}}protocol GreetingViewEventHandler {func didTapShowGreetingButton()}protocol GreetingView: class {func setGreeting(greeting: String)}class GreetingPresenter: GreetingOutput, GreetingViewEventHandler {weak var view: GreetingView!var greetingProvider: GreetingProvider!func didTapShowGreetingButton() {self.greetingProvider.provideGreetingData()}func receiveGreetingData(greetingData: GreetingData) {let greeting = greetingData.greeting + " " + greetingData.subjectself.view.setGreeting(greeting: greeting)}}class GreetingViewController : UIViewController, GreetingView {var eventHandler: GreetingViewEventHandler!var showGreetingButton: UIButton!var greetingLabel: UILabel!override func viewDidLoad() {super.viewDidLoad()self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)self.setupUIElements()self.layout()}func setGreeting(greeting: String) {self.greetingLabel.text = greeting}func setupUIElements() {self.title = "Test"self._setupButton()self._setupLabel()}private func _setupButton() {self.showGreetingButton = UIButton()self.showGreetingButton.setTitle("Click me", for: .normal)self.showGreetingButton.setTitle("You badass", for: .highlighted)self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = falseself.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)self.view.addSubview(self.showGreetingButton)}private func _setupLabel() {self.greetingLabel = UILabel()self.greetingLabel.textColor = UIColor.whiteself.greetingLabel.textAlignment = .centerself.greetingLabel.translatesAutoresizingMaskIntoConstraints = falseself.view.addSubview(self.greetingLabel)}func layout() {self._layoutButton()self._layoutLabel()self.view.layoutIfNeeded()}private func _layoutButton() { let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)self.view.addConstraints([cs1, cs2])}private func _layoutLabel() { let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)self.view.addConstraints([cs1, cs2, cs3])}@objc func didTapButton(sender: UIButton) {self.eventHandler.didTapShowGreetingButton()}}let view = GreetingViewController()let presenter = GreetingPresenter()let interactor = GreetingInteractor()view.eventHandler = presenterpresenter.view = viewpresenter.greetingProvider = interactorinteractor.output = presenterPlaygroundPage.current.liveView = view.view

Yet again, we back to the features:

  • Distribution — undoubtedly, VIPER is a champion in distribution of responsibilities.
  • Testability — no surprises here, better distribution — better testability.
  • Easy of use — finally, two above come in cost of maintainability as we already guessed. We have to write a huge amount of interface for classes with very small responsibilities.


So what about LEGO?

While using VIPER, we might feel like building The Empire State Building from LEGO blocks, and that is a signal that we have a problem. Maybe, it’s too early to adopt VIPER for our application and we should consider something simpler. Some people ignore this and continue shooting out of cannon into sparrows. We assume they believe that their apps will benefit from VIPER at least in the future, even if now the maintenance cost is unreasonably high. If we believe the same, then we’d recommend trying Generamba — a tool for generating VIPER skeletons.

That’s all about in this article.


Conclusion

In this article, we understood Why Are Architecture Patterns important in iOS. This article reviewed some popular ones and compare them in theory and practice going over a few tiny examples.

Thanks for reading! I hope you enjoyed and learned about Architecture patterns importance in iOS Application. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow other website and tutorials of iOS as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING!!???

Loading

Summary
iOS - Why Are Architecture Patterns Important In iOS Application ?
Article Name
iOS - Why Are Architecture Patterns Important In iOS Application ?
Description
In this article, We will learn about Architecture patterns importance in iOS Application. 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 we 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.
Author

Leave a Comment