“Wrapping third-party APIs is a best practice.” — Robert C. Martin

Intro

One of my colleagues told me that wrapping third-party libraries is a controversial topic, but I will try to convince you that it’s actually something worthy of consideration.

When?

Wrapping third-party library may benefit you the most in the following cases:

if you have multiple projects

if you work on a long-term project

if you use a library as a temporary solution

Sometimes it may be tricky to decide whether to wrap third-party library at the beginning or to postpone. I draw this simple graph to help you to decide. Basically, the more you defer, the more effort it will take in the future.

How?

There are several ways how we can wrap the library.

Basically, we dedicate a class, package or module to the wrapper.

For medium and large third-party libraries, I would recommend having a separate module. This approach has several advantages:

speeds up build (cache + parallel)

separates dependencies (all dependencies of the wrapper lay in a separate gradle file)

prevents from accidental usage of internal (app specific) code/resources

Benefits

There are several benefits which we can achieve by adding a wrapper on top of a third-party library. Some of them provide immediate value, while others focus on future values like reusability part of functionality and scalability of our app.

Define the API of the wrapper

A lot of libraries which we use were written in Java, so when we wrap them we can define our own API.

Let’s say we want to wrap Glide library, here is an example of this library APIs.

We can take advantage of Kotlin optional parameters and add some type-safety with sealed classes to make it easier and safer to work with image loader.

More flexible to changes

When we want to update a third-party library from one version to another or swap one third-party library with another, having a wrapper makes it a trivial task.

Breaking changes

If we take a look at the Glide library releases as an example, there are breaking changes in every major and minor version updates.

Sometimes these breaking changes affect us directly and sometimes not, but with an image-loader-module wrapper, in most cases, we only need to make changes inside this module and our main-module will stay untouched.

Migration to new technology

Nothing stays the same, new libraries are released every day. In 2014 universal image loader was a very popular library but in late 2015 author decided to stop development. Now we have Glide, Picasso, Fresco.

Once again, having an image-loader-module wrapper can help us to migrate from one library to another without the need to touch main-module.

Share common logic

When we make a wrapper for a third-party library we can also have some predefined configuration or provide some additional functionality.

Predefined configuration

In the case of image loader, we can have default values for things like a placeholder, transformation, animation so we don’t have to set them over and over.

Additional functionality

Sometimes third-party library solves a very generic problem and if we need additional functionality it may take some effort. We can put all additional functionality inside our wrapper and reuse across apps.

Loosely coupled system

Wrapping a third-party library helps us to create a loosely coupled system which is much easier to test and reuse.

Easier to test

Not every third-party library was made with testability in mind but adding a wrapper may help us to make our app more testable in places where this library is used.

Reuse modules across apps

One of the main benefits of having a wrapper for a third-party library is the possibility to reuse wrapper across other projects.

This means that when we fix an issue or add some feature in the wrapper module, all our projects will automatically gain the benefit.

Tips when making a wrapper

1. Don’t leak third-party library references in wrapper API, because the main idea of the wrapper is to avoid having direct dependencies on a third-party library.

2. Make the wrapper API as abstract as possible.

Let’s say you want to make a wrapper for the third-party library which simplifies work with Android permissions and you also want to have a reactive API. Exposing RxJava may sound like a good idea but what if someone wants to consume it via another reactive library? That’s where reactive-streams-jvm comes into play.

3. Wrap API which you use incrementally.

Don’t tend to wrap all third-party library API at the beginning, focus on things which you use or plan to use in the near future.

4. Put proxy on UI components.

Let’s say you use a third-party library which provides a custom view called IndicatorView . This IndicatorView is declared in XML and Kotlin/Java code.

To avoid direct dependency in the XML file you can wrap IndicatorView with your own custom view, but it may impact rendering performance, so instead, you can take advantage of view stubs or include tag.

To avoid direct dependency in the Kotlin/Java code you can create a proxy class (Presenter, Controller).

Adding proxy for UI components can help you to migrate from one third-party to another, or swap third-party library with your own implementation.

Conclusion

Despite the fact that wrapping third-party library may take some time it makes your system more flexible to changes and loosely coupled.