Reusable Behaviours in UIViewControllers

Nebil Kriedi
Engineering at Depop
4 min readSep 19, 2017

--

This post demonstrates an approach for writing reusable Swift code when working with UIViewControllers in an iOS app.

Introduction

My name is Nebil and I am an iOS engineer at Depop. My team develops and maintains our iOS application. Here’s how it looks:

Pretty much every screen in the app consumes data downloaded from a RESTful API, and as a result several behaviours are common and must be implemented in multiple view controllers.

What typically happens whenever a screen is presented is the following:

  • A loading indicator is shown when the first set of data is downloaded
  • If something goes wrong an alert is displayed
  • If no data is available we show an empty state
  • In case of lists, we automatically load the next page as the user scrolls to the bottom

If this sounds familiar then you might have come across the same challenges we have faced.

Challenges

  1. UI/UX Consistency: a given behaviour should always look and work the same way across the whole app.
  2. Code reusability: each behaviour should be implemented in a single place in the codebase.
  3. Flexibility: we want to be able to easily adopt any of these behaviours from any view controller.
  4. Customisation: we want to be able to easily extend the default implementation of any of these behaviours.

The Initial Solution

At the beginning we used inheritance with a base UIViewController implementing all behaviours.

We used this approach for quite some time, until we realised that things were getting too complex — the code was hard to maintain and to reason about.

Furthermore the base class was tightly coupled with the UITableView class, making it hard to extend to support other components such as UICollectionView.

We soon realised that composition was a design pattern better suited for our needs.

The Final Solution

Things became a lot easier once Apple introduced Protocol Oriented Programming (aka POP) using Swift.

Introducing “Presentable”

In protocol oriented programming you use a protocol to describe a particular behaviour. Here’s how we ended up defining the API required to present a loading indicator:

Provide a Default Implementation

Thanks to Swift’s protocol extension it is possible to provide a default implementation for our new method:

Swift documentation for “generic where clause”.

Adopting “Presentable”

Now any UIViewController can adopt the loading view behaviour described above simply by doing this:

All we have to do in order to show or hide the loading view is:

Note that in order to adopt this behaviour a consumer does not need to know the concrete type of the view controller, something really powerful when it comes to unit testing.

Working with protocols is a great advantage in architectures like VIPER where a screen is composed of smaller independent components loosely coupled and thus very easy to test.

Customising The Default Implementation

Whenever we need to go beyond the default behaviour we can simply override the method of the protocol and implement our custom logic.

In the following example we have a view controller with a custom refresh control which should stop refreshing once the loading spinner is dismissed:

Note that by calling defaultLoadingViewStatus(…) we can still invoke the original implementation defined in the protocol extension.

Combining Behaviours

We can combine multiple protocols together to describe a more complex behaviour that can be reused by several screens throughout the app.

For example, here’s how a view controller which loads a paginated list might look like:

Conclusion

We have been using this approach for more then a year now and we are very happy with the results.

We are now using this approach throughout the app and especially updating all our legacy code to this pattern.

We are very excited about the capabilities of Swift and Protocol Oriented Programming and we look forward to seeing how these features can help us solve more problems in the future.

We’re hiring!

You can check out our open roles here and follow our LinkedIn page. If you have a background in engineering but don’t see the perfect role for you, or just want to pop by our offices for a chat and a tour get in touch: work@depop.com

--

--

I'm just an iOS developer with a Fender Stratocaster. I work at @depopmarket.