Improving VIPER architecture with protocol’s extensions
Modules written using VIPER architecture can have multiple functionalities. A single module like „Edit Profile” can be responsible for fetching user, displaying some basic information, editing user functionalities and uploading a profile photo. There is a lot of approaches to combine all of it into a single module, and I would like to show you mine, which I’ve used recently.
I assume that you are familiar with VIPER’s basics. If you are not, please check some tutorials about it first.
Let’s start from basics. At the beginning we will prepare all module items including protocols and classes.
I skipped the entire module setup intentionally because I would like to focus on functionalities. At this point let’s assume that we’ve got all dependencies setup up correctly. Now it’s time to start adding functionalities. The first thing which we’ll take care of is getting a user from UserSessionManager. In the production app it would look like quite different, but to simplify the example let’s make it like that:
Now we need to combine all of those parts together. The whole task is really simple. Basically, we can just put dependencies and functionalities directly into our module. Unfortunately, that approach would be problematic later on — we won’t be able to reuse those parts of code in different modules. We can move it to base classes but that solution has another limitation — we can inherit only from a single class, so that won’t let us easily combine a few different functionalities into a single module. To resolve that problem, I will put functionalities inside additional protocols. Using protocol’s extensions, we can put default implementations of all methods there.
To include those functionalities into our „Edit Profile” module we just need to update our protocols. Looking at protocols we can easily get to know what kind of functionalities specific module has.
At this point there is only one thing we need to add to our classes — it user's data manager. In our example, it’s just regular data manager, but we need to set it to fullfill protocol conformance.
Basically, that’s all we need to do. Now we can use all functionalities (currently only user information) from EditPresenter just like below.
As you can see we’ve got all functionalities correlated to a user outside Edit Profile module which is a convenience regarding testing and reusing it in different modules. To expand specific module with the additional functionality, we just need to update our protocols, set require dependency, and we’re ready to go.
Similarly, we can prepare protocols responsible for editing user (please notice that we are using DataManagerUserInterface written previously since we need the same user session manager here). Just like before, it’s really easy to expand our module with that functionality. The only thing we need to do is to add another base protocols and extensions.
Expanding our module with that new functionality is exactly the same like before:
We can easily test those functionalities since it’s not put directly in a specific module. Let’s try to write some unit tests for InteractorUserInterface. We can use regular UserSessionManager, but in our example, we’ll use stub version of it.
Before we use it, we need to have stub version of data manager which will use that session manager:
Now we can create interactor which conforms to InteractorUserInterface but it uses stub version of UserSessionManager.
Thanks to this approach, my VIPER modules are really tiny — they contain only logic specific for a single module. All shared functionalities I’ve got inside protocols which let me combine them really easily. It’s also really, convenient because of reusability. I do not need to copy the same parts of code in different places. Last but not least: testability — since those protocols are not strictly correlated to a specific module, it’s really easy to cover it using unit tests just like in the example above. At this tutorial, I presented you how to expand interactors and data managers with new features but using that approach you can easily add additional functionalities for the rest module items like presenter and view interface as well.
I’ve prepared playground, which contains a whole example, including unit tests. Feel free to download it from here.
Join our software development team: https://www.untitledkingdom.com/careers