VIPER, RxSwift-ified

Daniel Tartaglia
7 min readNov 3, 2019

I have committed a new repository as an example of using Rx to implement the VIPER architecture. This is another one of my “follow along with the commits” articles which means that you should back up to the first commit and examine the code from each commit while reading the associated part of the article.

As far as I know VIPER was first described in Architecting iOS Apps with VIPER back in June 2014. I decided to revisit this pattern and see how it can be adapted to live in our new, more functional and reactive world.

I have re-implemented the sample that came with the article above, but I also want to take this time to discuss a bit about the process of developing a program with the idea of documenting some of the non-code processes involved in creating an application. (This first part is a reprise of my previous article with specific details about this particular project.)

Definition Phase

For the purposes of this application, we only have one user type and one entity type. The former we will call User and the latter will be ToDo. Now let’s consider the actions. Since this is just a sample app we will only allow the create and read actions. Here are our user stories.

  • As a User, I can create a ToDo.
  • As a User, I can read all ToDos due this week and next.

Design Phase

In our case, we are expecting two screens. We will call them the List and Add screens. In the process of drawing them out, we have identified another user story as well as steps for all of them. The new story acknowledges the fact that creating a ToDo is a multistep process from the user’s perspective and gives the user the ability to abort the create action.

(-) As a User, I can create a ToDo.
From the List screen:
1) Tap the add button to display the Add screen.
2) Enter a title and date of a ToDo (both are required.)
3) Tap save button to save ToDo and dismiss ToDo screen.
(-) As a User, I can cancel the create ToDo action.
From the Add screen:
1) Tap cancel button to dismiss Add screen without saving.
(-) As a User, I can read all ToDos due this week and next.
From the List screen:
* ToDos should be in date order.
* ToDos should be grouped in the following categories:
Today, Tomorrow, Later This Week, and Next Week.

Development

We have a solid understanding now of what the user can do and we know what order we are going to develop those features in. We also have detail designs for the screens needed (at least for the first user story) and a list of fonts and colors that will be used throughout the app. Now it’s time to start development.

Before talking about how to adapt VIPER to Rx, I want to clear up a misconception about the architecture. Some people assume that there must be a VIPER (View, Interactor, Presenter, Wireframe) stack for every screen, but that is not at all true. Sometimes a single screen might be involved in multiple user stories at the same time and so will have several interactors. Sometimes a screen might be used for several different stories and have multiple Wireframes and Presenters associated with it. Lastly, sometimes multiple screens might be involved in a single user story and then one Wireframe will display several screens.

Story 1, Step 1: Tap the add button to display the Add screen.

In the first commit, I am implementing the first step of the first user story, “Tap the add button to display the Add screen.” Of course a lot has to be done for this simple step because in order to tap that add button, we need an application. Since the user story starts on the List screen, we need to create that as well, but only in so far as to give it an add button. Notice I haven’t fleshed out the rest of the screen. I went ahead and laid out the entire Add screen since I know I need it for the rest of this story. Also, since this is the step that displays the Add screen and the detail-design calls for a special presentation animation I did that here.

With the original conception of VIPER, each component was a class with several functions. In Rx things are a bit different. Here each component tends to be a single function. So I have the files named based on the component they represent, but each file only contains a single exposed function and possibly some private helper functions.

When the app starts up, the flow is as follows:

1. The ListWireframe is given control.
2. Since the storyboard has already displayed the List screen, the wireframe only needs to install the presenter into the view controller’s buildOutput function.
3. The ListViewController eventually loads, creates its Input object and then passes that input to the buildOutput function.

When the user taps the add button, the flow is as follows:

1. The addItem emits a next event to the list presenter.
2. The list presenter maps that to an add ListAction and emits that to the wireframe.
3. The list wireframe catches the add action and calls displayAdd which is in the AddWireframe.swift file.
4. The add wireframe instantiates the add view controller and presents it.

Story 1, Step 2: Enter a title and date of a ToDo (both are required.)

Much of the functionality of this commit was actually added in the last commit since I laid out the Add screen. However, to help ensure the stipulation that both title and date are required for saving, this is a good time to make sure the save button is only enabled if there is valid input. This is our first real bit of logic so I went ahead and added tests. Tests aren’t often added to sample code so I thought it would be a nice touch.

You will see that the logic is all in the presenters so that’s what is tested. In a properly constructed VIPER system, all of the logic will be in the presenters and interactors. I expect to have a test case for each user story.

Story 1, Step 3: Tap save button to save ToDo and dismiss ToDo screen.

For this step, we created our first interactor. We have also discovered a path not mentioned in the user stories. We need to outline what happens if the save fails. As mentioned, we update the user stories with this newly discovered information.


(-) As a User, I can create a ToDo.
From the List screen:
1) Tap the add button to display the Add screen.
2) Enter a title and date of a ToDo (both are required.)
3) Tap save button to save ToDo and dismiss ToDo screen.
Error: If the save fails, instead of dismissing the ToDo screen,
an Alert screen displays explaining the error.

One of the defining features of VIPER is that only Interactors work with Entities and the DataStore, the Wireframe injects the DataStore into the Interactor, and the Presenter knows nothing about the DataStore or the Entities.

You will notice that the presenter doesn’t create a Todo object, instead it just passes enough information to the Interactor for it to create the Todo object which then passes it to the DataStore. Then the DataStore converts the Todo object into a form that is savable and saves it.

It’s interesting to note that up to this point, our code has been indistinguishable from an MVVM-C architecture (other than the names of things.) VIPER adds an extra level of indirection between the interactor (view model) and the entities/data store. In MVVM-C, the view model works directly with them both. Personally, I’m not sure if this extra level of indirection is useful. Even in an application this small, I’m left wondering where I should change the date of the object to the startOfDay? Should it be done in the presenter or in the interactor?

Story 2, Step 1: Tap cancel button to dismiss Add screen without saving.

Since the screen and button for this story have already been laid out, it’s just a matter of hooking them up. We add the cancel button to the UI’s input, and then bind it directly to the wireframe’s Observable.

Story 3: Read Todos.

An important thing to notice at this point is all the different ways a “Todo” is represented in the system, even in something as simple as this example program.

  • There’s the way the DataStore represents todos internally (in our case, as a plist structure and if we had used CoreData then as NSManagedObjects).
  • There’s the way todos are represented internally by the interactors (and externally by the DataStore) as Todo structs.
  • There’s the way todos are represented internally by presenters (and externally by interactors.) The list presenter uses UpcomingItem.
  • Lastly, theres the way todos are represented internally by view controllers (and externally by presenters.) The list view controller uses UpcomingDisplaySection and UpcomingDisplayItem.

Again I am struck with the difference in complexity between MVVM-C and VIPER; in the former there would only be three different representations of a todo rather than four. Maybe in a larger project it would make more sense; presumably we would find re-use opportunities that wouldn’t otherwise exist? In MVVM-C I’m inclined to create such abstractions when they are discovered rather than assuming them at the outset.

For this story, you will see that most of the changes/additions are mostly dedicated to morphing the data in the stored plist into data the table view can understand over the four stages mentioned above. I also had to modify the AddWireframe so it could notify its parent about successful updates and pass that into the List presenter so it will know when to re-get the list of items.

Epilogue

This was an interesting exercise and I learned a lot about both VIPER and MVVM-C while doing it. Remember, the point of this article is to follow along with the commits. If you aren’t doing that, you won’t get the most out of it. Be sure to check out the repository and follow along.

--

--

Daniel Tartaglia

I started programming as a hobby in the late ’70s and professionally in the late ’90s. I’ve been writing iOS apps since 2010 and using RxSwift since 2015.