Prefer Composition over Inheritance: Viper Module Case Study

Emre Ergün
Trendyol Tech
Published in
4 min readApr 8, 2020
Photo by Gabriel Crismariu on Unsplash

Hello everyone, this story will be about couple of techniques we used in viper architecture, specifically avoiding code duplication while making module helper methods as reusable as it can be.

It is assumed that you are familiar with the Viper Architecture. If not, you may need to learn something about it first. There are plenty of resources since it is popular nowadays. 💪🏻

As you know, each layer in the viper module has an interface.

Assume that you have started a project and you decided to use VIPER as project architecture. Let’s say HomePage is your first module, then probably you want to create four layer-class (View, Interactor, Presenter and Router) and make implementations according to the VIPER. Let’s focus on the view layer (aka ViewController);

You defined public methods at the interface and implemented these methods at the place where your view controller conforms to the interface. Seems nice and clean, nice job. Then you started to next module, let’s say it is the ProductDetail Module. Like a view layer above, it’s view layer should be the something like below;

It looks nice too, but you have noticed an issue here. If you look at defined methods, some of them are defined exactly twice. Oh, there is some kind of duplication here. As a smart guy, you want to refactor it. Thankfully you know what object oriented programming is, and you have decided to apply it here.

Since each view layer is an instance of UIViewController in our example, you can remove these duplicated methods from view interfaces and define at the common UIViewController class. Let’s say it’s RootViewController;

Now you can make ProductDetailViewController and HomePageViewController inherit from the RootViewController. However it does not fit your current VIPER architecture since layers communicate each other using only layer interfaces. For example, for the HomePageModule, HomePagePresenter holds a reference of HomePageViewInterface, not the HomePageViewController itself. If you want to call setTitle(_) method from presenter, you will get a compile time error.

Causes compile time error, for now.

Actually it’s obvious, since HomePageViewInterface knows nothing about the RootViewController, methods you have implemented in the RootViewController are not accessible from presenter. How should you solve this ? Thanks to the swift, inheritance applies not only for classes, but also for protocols.

So, you have created a BaseViewInterface instead of BaseViewController for that purpose;

Then you conformed to BaseViewInterface for both HomePageViewInterface and ProductDetailViewInterface. There is one last step left. You should make default implementations of these methods at the BaseViewInterface extension;

You may wonder that why we use Self: UIViewController part. Its called protocol constraint, if you don’t add UIViewController constraint and try to use UIViewController related stuff here (for example navigationItem in the showLoading method), you’ll get an error. Because without UIViewController constraint, compiler cannot know which class actually conforms to this protocol when the program control reaches here. For further info, you can take a look at the Adding Constraints to Protocol Extensions part of the document you can reach using link below;

It seems so far so good. Now there is no code duplication left, and you can add all common methods to BaseViewInterface in the future.

Is it perfectly okay ?

True, mostly. I am sorry to say that, however there is one last problem left. Actually it is not a problem, but as a smart developer, you can design it better. As the project gets bigger and bigger, BaseViewInterface will grow too. At some point, maybe you want to use just one method from BaseViewInterface, however by conforming BaseViewInterface you will apply many of unnecessary methods too. Clearly it does not fit to SOLID principles, especially in terms of separation of concerns.

Let’s focus on how we can make our design better. Actually, most of methods you defined at the BaseViewInterface can be grouped. For example, take a look at two methods defined at the BaseViewInterface;

Each time a view layer should do something with urls, you can use these two methods. What about grouping these into a specific protocol instead of BaseViewInterface ? Let’s do this, remove these two methods with default implementations from BaseViewInterface to new protocol and call it UrlOpenable;

Now anytime you want to use these methods, you can simply make your class interface conforms to UrlOpenable. For example lets conform it for HomePageViewInterface we defined before;

Now you can call openUrl method from HomePagePresenter through view interface. It will work well as before. We can create these task-specific methods as much as we need; UrlOpenable, AlertPresentable, NavigationBarConfigurable, LoadingShowable

If you want to use a method from specific protocol, just conform it, super easy. In this way, you don't have to conform a god protocol for using just couple of methods. Actually, by making these changes, there should be no god protocol left 🙃

Thank you for reading, see you in the next story!

--

--