Skip to content

Building boundaries with third party libraries

The joys of jet lag. Here I am, at 2:43 am, trying to finalize a post that I started writing a couple days ago, while flying over the Pacific Ocean.

This is about dependencies, those third party libraries that often provide out of the box solutions to complex problems we need to solve.

Importing a third party library will save time and effort. Just add a line to a pod or Carthage file, and boom! We have saved days, weeks or months of development.

There are multiple benefits in relying on a third party library. Popular libraries are often well tested, and contain the knowledge of many developers about a given problem. Third party libraries save development time. Third party libraries avoid reinventing the wheel.

But third party libraries are code that you have not written, code that you don’t actually own, but that you somehow have to start maintaining as soon as you import it into your codebase.

Let’s take an example. Loading images, asynchronously, without blocking the main thread, in a table or collection view cell is a complex problem. We need to cancel image downloads when cells are not visible anymore, we might need to handle cell reusability, and ideally we should cache images. None of those problems are trivial to solve, and might require time and finesse to get right.

On he other hand, we could just import a third party library, like, for example, Kingfisher.

All we’d need to do would be adding a line to a pod / carthage file.

But…

We have created a subtle problem here: our cell’s code depends on Kingfisher. We need to call a specific method in that library, passing a set of specific parameters, in the way that library expects.

That be not be a problem if we only have one cell. But things start getting dangerous as soon as we have more than one cell.

Let’s say there are 10 different cells that present images in our codebase. Kingfisher is so convenient that we will repeat the same code in the 10 of them.

What would happen if, for some reason, Kingfisher stops being maintained? Or a new version of Swift breaks source compatibility again, and this library does no get updated? Or a new library comes by, that is twice as efficient as Kingfisher.

Then we would have a bit of a problem. Our code is strongly coupled with the specific api of a very specific library, and updating our code to use a different library might require getting into shotgun surgery mode.

Shotgun surgery.

How do we switch? Well, we could just do a search and replace, or search all the calls to Kingfisher and update them to use the new, hottest library.

But that is not very efficient, obviously.

So how do we avoid the problem?

Building boundaries

The same way that we have tools and mechanism to decouple some parts of our code from some other parts of our code, we have tools and mechanisms to decouple or code from third party libraries.

And the best tool for decoupling things is, as usual, a layer of indirection.

In this case, we could add a layer of indirection by encapsulating the call to the third party library in an extension to UIImageView. This extension would be code under our control, and will contain a single point of dependency with the third party library.

Our cells will load images by calling a method that belongs in our side of the codebase, not in the library’s side of it.

Now if we want, for whatever reason, to switch to a third party library, all we have to do is modify the implementation of our extension. Import a different library, update the presentImage method to that third party library’s API, and we are good to go.

Final words

Introducing seams between our code and third party libraries decouples our code from external dependencies. That way, we are free to modify those dependencies without having a big impact in our codebase.

PS

Kingfisher is a great library that solves a complex problem in an elegant way. I strongly recommend considering it.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *