Design patterns I love - Interceptor assembly for mobile apps

Setting all the tools in order before beginning to work. This is the mantra i learnt from lean software development principles.

.

Some time ago our team inherited an android app code base that used to do a humongous amount of tasks. A lot of modules, a lot of functionality, and a lot of checks and controls all over the app. It was a spaghetti mess and too many checks and controls had actually made the code too difficult to read. What was most disturbing among all was the places where API calls were made. Any API call had the following things:

  • Check if there is active Internet
  • Encrypt the payload, decrypt the response
  • Add an additional header based on some conditions
  • The actual API call
  • Error handling for 401
  • Error handling for other custom errors.

Imagine all of these steps being performed in each and every API call! No wonder it ended up messy and almost impossible to maintain. So is this the end? Or there is another way to do this in a clean way? Why can't we focus on business logic and let the architecture take care of these nitty gritty stuff?

Welcome to the world of interceptors.

What is an interceptor?

As the name suggests, an interceptor is something that intercepts an inflow or an outflow. In our context it intercepts a http request/response to perform something on it before it is sent over the network or sourced from the network. This sets the rules that are executed in each and every API call from the application. Interceptors are now widely used in mobile apps as well as web apps.

A little twist in the tale

There were a lot of design patterns available to consider. I have been using many design patterns throught my career. However, this scenario required a slight innovation in existing patterns. So I decided to use Interceptor Assemby design pattern. So, now I could tailor it to mobile apps that need lot of interaction with RESTful API. As you can guess, it adds a chain of interceptors to act in a sequence. As the request/response flows through these interceptors, it gets progressively enhanced.

A concerte example

In this article I will present a working solution of one interceptor that takes care of adding an extra header in the request, allowing the developer to focus on the business logic. For the sake of example we will use OkHttp as our http client, but this feature is supported in any standard http client at market.

Create an interceptor that adds an additional header to the request

public class AddHeaderInterceptor implements Interceptor {    
    @Override
    public Response intercept(Chain chain) throws IOException {
       Request request = chain.request();
       Headers headers = request.headers();
       request = request.newBuilder().headers(headers).addHeader("User-Agent", modifiedAgent).build();
       return chain.proceed(request);
    }
}

Code is a cake walk

The code is pretty straightforward. This implements Interceptor given by OkHttp and uses it to add a new header to the existing request. Then by calling chain.proceed(request) it actually hits the API with the modified request.

The critical piece

The call to chain.proceed(request) is very critical as this is the place where the actual http call is executed, providing the response.

The next interceptor in the chain

In this example now we have a working interceptor. How about another that applies an error handling policy for 401 errors? Generally these are caught in the error handling section of the API call and these gets repeated throughout the application. If we move that as well to interceptor, then behavior of all API calls can be controlled from one single point. Let's have a look at that interceptor.

public class ErrorHandlerInterceptor implements Interceptor {    
    @Override
    public Response intercept(Chain chain) throws IOException {
       Response response =  chain.proceed(chain.request());
        if (response.code() == 401) {
            // Handle the error as per business logic
            
        } else {
            return response;
        }
        ...
    }
}

Now we have two interceptors that can be chained to achieve the functionality. Interceptors are executed in sequence and they are executed in the order they are added to the client. Before coming to that let's see how an interceptor is added to an http client (here OkHttp).


OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new AddHeaderInterceptor())
        .addInterceptor(new ErrorHandlerInterceptor())
        .build();

Retrofit api = new Retrofit.Builder().baseUrl(BASE_URL).client(client).build();

What we acheived

In this example we have chained two interceptors and placed them in the desired sequence. So as per this example the header will be modified first and then when the response is received, the error handler interceptor will be in action. It is the job of a developer to consider the order of interceptors for execution. OkHttp uses list to track interceptors and they are executed in order.

With Great Power Comes Great Responsibility

Remember this quote from Marvel comic's pages? Later it has been attributed to many fictional and actual characters, but that is a different topic altogether. Why this topic is relevant here is, interceptors give full control to intercept and modify request and response. These are basically the gatekeeper standing between the application and the network it connects to.

Interceptors are very powerful

We can change headers, change payload, even change response and bypass server errors, or even intercept server error and modify to something else so the app can handle them, and the possibility is endless. Justifiably as I mentioned earlier, be responsible for things done in interceptor.

Where we stand now

In case you are still thinking what happened finally to the applications that we inherited, we introduced interceptors and viola! a lot of lines of codes disappeared from almost all places wherever we had any API call. Soothing to my eyes, and way easier to maintain.

Last words

Have you also used interceptor in your projects? What was the most innovative way to use an interceptor according to you? Let us know in the comments.