SECRET OF CSS

Implementing the Viper Design Pattern in iOS | by Mehmet Ateş | Sep, 2022


A step-by-step guide with an example project

0* d2wt7RvAdtIAFMt 300w
Photo by Lala Azizli on Unsplash

Design patterns! In the software world, they are indispensable for us to be able to work on sustainable code bases that prevent us from writing code on our own. What about Viper? It’s a popular design pattern we use to develop iOS apps. So, let’s get to know it better and embark on a long-term adventure.

View, What else could it be? The layer that contains the User Interface elements and can only communicate with the Presenter.

Interactor (Optional), This layer connects us to real life and again only communicates with the presenter.

Presenter, Viper’s apple of the eye. The layer where everything happens. In short, our bridge connecting all layers.

Entity (Optional), Our models. Seriously only models. Our structs or classes.

Router, Viper’s bigbang. Our brave layer is in charge of the creation of the module and all layers (including itself).

We met our layers. Yes, yes they are also very pleased to meet you. So we are ready to answer this.

No slander or praise here. There are only facts.

Let’s start with the positives.

  • Viper is incredibly testable.
  • Viper provides the code layout very well.
  • Readability is very high.

Let’s look at the downsides.

  • Viper is very bulky. Too many layers too much time!
  • It is complex. You can get lost in folders. Or you find the exact path.

Check out these items now.

  • You want to release an app quickly. Really fast. For example with SwiftUI. It would be better not to use it.
  • You have embarked on a huge project with UIKit. It needs to be tested. Definitely use it.

Viper is a headache if you’ve never done protocol-oriented programming. But once you get used to it, it goes away. This article is for exactly that. Let’s roll up our sleeves and get started.

I wrote an application for this, while you are reading the article, you can examine it and follow how the code works.

I think our first job is to complete the view layer that we always do. Here I created a simple list view using the compositional layout of the UICollectionView. Even doing this I will need the Presenter but for now let’s do it in the view controller.

The code you see below is definitely not suitable for Viper ❌

Collection View Base

The above code gives the same output. Cells have been successfully created and a base has been prepared. Now let’s pass this code to Viper by keeping the view fixed. As I mentioned earlier, Viper uses P.O.P quite actively. Therefore, we define all view operations in the presenter in the interface of the view. The purpose of this is to be able to access from the presenter. Let’s take a look at the edited version.

You are looking at a huge piece of code and you may not understand what would have changed. First of all, make sure that this code gives the same output. We currently have a presenter that does not appear in the background, and this can be confusing. But worry not! Everything will be clear soon.

  • The first change, of course, protocol. We added 2 functions and defined in ViewController. What we need to do now is to call it in the presenter wherever we need it. This is currently presenter.notifyViewDidLoad()
  • The second change is the numberOf functions. These functions may vary by server or local services. So this number should be passed to the view by the server. This way, if there is a change, the view can be aware of it.
  • The last change is the setTitle function. The reason for transferring this to the presenter is actually the same. Because we can set this by the services.

As you can see, what we’re actually doing is separating all logical operations. And at the same time, we have written code for the future. Although this slows it down at first, it will speed up a lot in the future. And remember. The view is not finished yet. Now let’s write a service and create our Interactor layer.

Since these are related to each other, it would be best to process them together. Interactor connects with the service and processes the model for us. We will have a very simple model and a service tasked with reading it.

As you can see, we have a fairly simple model and registration system. It has functions for both reading and writing. To get the point across, think of it this way, this is the layer where we communicate for backend operations. We fired exactly one request and we’re getting an array back.

As you can see, nothing complicated. We first recorded our data and then read it. Thus, Interactor fulfilled its own responsibility. Now let’s come to where everything connects, namely the Presenter.

Important note: You can ignore line 16 here. Its only purpose is to create a data for you on the first run of your application. If you want, fork the application, run it once and delete this line. The result will be the same.

We’ve really come to the layer where almost everything happens. This layer should now look like this.

Slow down here!! This layer is the most important layer for the viper, and we have questions to ask. These are the questions we will ask why. Let’s explain our layer slowly.

See the protocol. It has all the functions we use on the view controller. It is defined in the interface setup and it is clear what they do. Perfect!

Take a look at the NotifyViewDidLoad function. “view?” We see the expressions. These are actually the functions in the interface of the viewController that we made at the beginning. And they are triggered in the presenter and work great. Everything seems to be explained. Now let’s move on to our questions 🙂

  • Why are variables private? The reason is that the variables represent our classes. And it wants us to perform viper logic operations on the presenter. If we leave it to public access, we can directly access the router or interactor through the ViewControler. We shouldn’t be able to do this.
  • Why is the view weak while the others are strong? The reason is the retain cycle. The presenter is defined strongly on the view controller. If we also define view as strong on the presenter, these two classes will never leave each other. And that’s a lousy experience for our memory management. What about the others? Do you think this applies to the interactor? Presenter is not even defined. If we keep it weak, the interactor will not be able to find a branch to hold on to and will disappear 🙁
  • Well, I understand the classes, but why is datas private? Remember it’s there for presenter logic. This is related to our first question but not exactly the same. If we open this data as public, we are more likely to “inadvertently” perform logic operations on the View Controller. Like getting the first data 🙂

I think we got through the hardest part of the job. If you are confused about the retain cycle, I suggest you look into it. Now then, we can create our final layer and make our final edits.

This is where the whole page starts. It will create all the layers and give a view for us. It should look like this for now.

Come on, what is this? Why is the protocol empty? Why do we keep it empty? In fact, it’s completely ready for use when we need it, for the reason. And remember, the presenter is created through interfaces. Classic init operations and a fallback viewController.

But, then the router is ok too. Where and how will I use it? I prepared a tab bar controller for you. Of course, it was created with viper too. We have to do something in the tab bar controller. Like initializing the ViewController?

Don’t get confused here. Just focus on line 18. We saw our router. We have run our function. And we started a Viper Flow. Now all the codes we wrote are active.

We have done a lot of operations, but we still haven’t processed our data into the view. Now it’s time to define our view. There are only 2 steps we need to do for this.

  • The first is to get the data by index in the presenter.
  • The second is to use this data in the cell configuration.
protocol HomePresenterInterface: AnyObject {
...
func getDataByIndex(_ index: Int) -> BaseModel?
...
}

Let’s add this function to our protocol. So we can get a base model by index. Xcode should be mad at us the moment we add this.

1*D1CrjYm7dw vYaSXosiGlQ

Let’s complete the function without further angering Xcode.

func getDataByIndex(_ index: Int) -> BaseModel? {
datas?[index]
}

Good, now let’s come to the view part.

cell.configureLabels(with: presenter?.getDataByIndex(indexPath.row))

As you can see it is one line and works flawlessly. Now let’s open our application. We should be seeing our data.

Fantastic! Thus, we have completed our application. Of course, if we wrote Viper, not if we didn’t write tests for our app. Come on, you’re not tired. Let’s also write our unit tests and it’s all over.

We’re going to do a few really cool things for this part. I want you to review this link. I think you can install it easily. It’s pretty simple. If you have successfully completed the installation, let’s include our project and continue. Before that, let me explain why we do this.

We copy the classes to be tested with the mock concept, and thanks to the generator we have set up, we can perform the test operations in seconds with the help of protocols. So let’s do it.

Are we suddenly stunned? Don’t worry, this is a file that you will create automatically. So it was created by the mock generator linked above.

If you don’t understand how it’s done this way 🙂

1*L Azggtg6wLc EcjzfiZBA

Now let’s talk about why we create it and how we will use it. Take a look at the interactor file to remember. There is only one variable in the protocol. And in the mock file, the variables required for testing this are kept. It’s normal to be confused here. Now, when I create a unit test and elaborate on it, you will understand it very clearly.

Here you may notice that other classes have mock classes. Except for presenter only. We named it Viper’s apple because we do everything here. We named it Viper’s apple because we do everything here.

Now understand this phrase. By giving our mock classes to the router, we enabled the layers defined in the presenter to be mocked. In this way, the view class triggered via the presenter will be Mock and we will be able to test it. Because the purpose of unit tests is to check if a function triggers a class.

Super, now let’s review our test. First, the presenter function is called and the view mock class is triggered. so the variable in our mock class changes and our test is successful. Let’s take a closer look at this function.

func testNotifyViewDidLoad() {
homePresenter.notifyViewDidLoad()
XCTAssertTrue(homeView.invokedSetupView)
XCTAssertTrue(homeView.invokedSetTitle)
}

In our first line, we trigger a function from the presenter. This function is exactly like below:

func notifyViewDidLoad() {
view?.setupView()
view?.setTitle(with: "Home")

fetchDatas()
}

As you can see, our function is triggered by 2 functions defined in view. Here are the functions in our mock class! For this reason, we go and check that the variables in our mock class are true so that we can make sure it is triggered. This is our target. To see it much more clearly and understand the event, you can update the function yourself as follows and renew the test.

func testNotifyViewDidLoad() {
XCTAssertFalse(homeView.invokedSetupView)
XCTAssertFalse(homeView.invokedSetTitle)

homePresenter.notifyViewDidLoad()
XCTAssertTrue(homeView.invokedSetupView)
XCTAssertTrue(homeView.invokedSetTitle)
}

The test will still be successful

And you entered the world of viper with fast steps. Fork the project so that this entire article comes to mind. Then play around and try to discover the changes.

There are a few situations I want you to experience.

  • To better understand the concept of weak on the presenter, add and delete weak. Keep track of references and objects with Breakpoint.
  • Edit the Interactor, Entity, and Service. The stage is yours. Can you print a data from the Internet like this? It would be nice to have things sortable 🙂
  • Create a sorting algorithm. This will make it clear how you use the presenter.

You did all this. And you want to make sure it’s correct. Don’t worry, of course I’ve thought about this as well. Check out the branches! You will see a branch named advanced. Go checkout and check your solutions.

1*aEjHm dDSBGG7ei

In the advanced branch, the application will appear in this state. The data comes and is updated entirely over the internet. There is so much you can improve even more. You can try 🙂

An article that I definitely recommend and take as an example.



News Credit

%d bloggers like this: