Our iOS modularization story

Martijn Schoemaker
Jun 21, 2018 · 8 min read

Some years ago … As a well-known retailer in The Netherlands for decades, wehkamp had already successfully made the transition from a catalog company to 100% e-tailer. Now it was time to explore the app universe at wehkamp.

We started with a SCRUM team with 1 Android developer, 1 iOS developer, 1 tester, some back-enders, a Scrum Master and a Product Owner. We filled our backlog and started building the app(s) from scratch. The apps were backed by a .NET API which was running on on-premise IIS servers. The .NET environment was already running for a significant amount of years now and has evolved into a big ass monolith.

Microservices and micro-site

The early days

At the moment … our team has 3 iOS developers and 2 Android developers and we are still searching for more.

History repeats

Working in a one-man-team it is not that necessary to think about maintainability, scalability, readability a lot. Of course I knew that the day would come that the team would grow, so I tried to implement every part of the app with this in mind. But sometimes the pressure is that high, you take shortcuts. It’s impossible to avoid this, but I tried to take those shortcuts as less as possible.

Inspired by the other teams creating microservices and micro-site and these guidelines, we decided to refactor our application and go modularizing! This is our story …

The Goal

Big ass monolith

To this:

Modularized app

But let’s first tell something about our starting point. This of course impacts the steps to take into the modularization direction, it makes it easier so to say …

Dependency Injection FTW

One principle that really helped me with this is Dependency Injection. I won’t go in much detail on this, there are a lot of sources available. But the idea is that you inject instantiations to a class instead of creating those in the class itself. This way you separate responsibilities instead of tighten things up, loosely coupled vs tightly coupled. I use dependency injection in combination with Protocol Oriented Programming where it makes sense. This approach kind of forces me to avoid coupling parts of the code that shouldn’t be coupled at all.

Protocol-oriented programming in Swift — Apple WWDC 2015

Features

Features already organized per feature in groups

This in combination with dependency injection resulted in pretty independent parts of the code. Every feature has its own storyboard (or sometimes multiple to prevent storyboard-team-merge-conflict-hell) and all other stuff related to that feature. As you can see in the picture there are for example features ‘Shopping’ and ‘Favorites’. The feature ‘Favorites’ shows us a list of products and you can imagine that a user wants to see product details when tapping on a product in that list. The product detail viewcontrollers are in feature ‘Shopping’. To achieve this I added a storyboard reference to the Shopping storyboard into the Favorites storyboard. And … gone is our separation!

First step; Core and CoreUI vs independency

Product overview

It’s a collectionview with cells that contain an imageview and some labels.

Now imagine that we want to add a new awesome feature to our app; a wishlist! The designs show us that it’s basically the same design as we already have for a ‘normal’ product list. So my first thought; let’s re-use that! To do that we create a class ProductListCollectionView. We have to define a datas ource, so let’s create a protocol ProductListCollectionViewDatasource. On the existing productlist we have a heart icon to add a product to wishlist, but we don’t want that if you are in the wishlist. So let’s add a protocol ProductListCollectionViewDelegate with a method shouldShowHeartIcon.

Wow! That’s working like a charm. Some sprints later we want to add an icon in wishlist to add the product to the basket. It’s an A/B test so we don’t want it on the product overview for now. We should add an extra delegate method shouldShowAddToCart. You can imagine that this will evolve into an unmaintainable, ugly, if-else polluted giant. That in the end, you don’t have a lot of common UI left. We should have started creating it as an independent element without sharing things. Don’t feel bad to sometimes copy-paste from one feature into another.

But there’s a trade-off. If you find yourself copying a specific part of code into all features, this could be a signal that it’s valid to create something to share this code. This is where modules ‘core’ and ‘coreUI’ come into play.

These are the modules where you put code that is used by all features. Sometimes it’s hard to decide if a specific piece of code should live in ‘core’ or that it needs its own module. I decided to put these kinds of things in core:

  • Networking
  • Security
  • AB Testing
  • Analytics
  • Configuration
  • Utils
  • Extensions on Foundation classes
  • etc.

The same count for UI elements, things that you will find here are:

  • UI elements
  • Collectionview layouts
  • Base ViewController
  • Input validation
  • State full ViewController
  • Extensions on UIKit classes
  • etc.

A thumb of rule for myself is that we should put as less as possible code in these modules, copy/pasting every now and then isn’t that bad…

Features

  • UI; with all UI related files; ViewControllers, Views, Storyboards and NIBs.
  • AB Tests; here we define AB tests we use in this feature.
  • Handlers; here are implementations of handlers we can define. For example, it’s possible to register a handler for ApplicationContinueActivity delegate this way a module can decide itself if it should handle this and if so what to do.
  • Model; for all model classes
  • Network operations; we define our network request as HttpOperations. HttpOperation is a protocol, for a specific request we implement a HttpOperation, here you can find these operations.

In other words, all stuff related to the feature should be in the feature module, not more not less.

Depedencies

We implemented the features in separate modules and ended up with multiple modules that don’t know anything about each other anymore. But we still want to be able to open that product detail screen when tapping on a product in the wishlist.

But … how? All these dependency magic is done by adding a ‘man-in-the-middle’ module; dependencies. This module will take care of the dependencies between features. Features don’t reference each other but only reference that dependency module. The dependency module shouldn’t contain any implementation classes. It’s only glue between features.

If we want to open a product detail view when tapping on a product wishlist we need something like a ProductViewControllerProvider

So what we could do is introduce a class ProductViewControllerProvider in dependencies which can provide the wishlist module with the right viewcontroller so it can show it. But this way we give dependencies module too much knowledge about the implementation. It shouldn’t know how to create a viewcontroller to show a product. The only one who knows this is the Shopping feature where the viewcontroller really lives.

So we need some abstraction here …

Only protocols

For our product detail from wishlist example this results in something like this:

  1. In Wishlist feature, we implement the list of products that are on the wishlist.
  2. In dependencies we have a protocol ProductViewControllerProvider with method getProductViewController() -> UIViewController.
  3. In shopping feature, we have the implementation of ProductViewControllerProvider that knows how to create the viewcontroller and returns it.
  4. Via dependency injection, we define in shopping module that for protocol ProductViewControllerProvider the implementation in shopping module should be used.

What now?

Happy modularizing!

Sample app

https://github.com/martijnschoemaker/modularizesampleapp


Thank you for taking the time to read this story! Feel free to leave your comments and ask questions! And share your own experiences with modularizing your app.

wehkamp-techblog

We'll try to keep up and post on the stuff we're doing and discovering. Interesting in working @wehkamp? Check out https://werkenbij.wehkamp.nl/

Martijn Schoemaker

Written by

EportaConsult.nl, App development, web services, Swift, .NET (core), ASP.NET MVC, pwc.nl

wehkamp-techblog

We'll try to keep up and post on the stuff we're doing and discovering. Interesting in working @wehkamp? Check out https://werkenbij.wehkamp.nl/

More From Medium

More on Programming from wehkamp-techblog

More on Programming from wehkamp-techblog

There can be only one

Related reads

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade