Safe Retrofit calls extension with kotlin Coroutines for Android in 2021 — Part III
So this is it. The end of this small series. If you haven’t seen the first two parts, here are the links to it.
Quick Recap!
First of all, congrats for having progressed this far 👏.
- We needed an extension for make retrofit calls safety ✔️.
- We needed a way to parse the exceptions and return them into human readable objects✔️.
- We needed to return a a success or a failure as result✔️.
And now we are going to add our middleware. This functionality will allow us to prevent the execution of the retrofit calls if any of its conditions is not supplied. Something like
if(middlewareConditionsAreSupplied) executeRetrofitCall else NOT!
Middleware Requirements
- Extendable — Our app can have more than one Middleware. We could have a middleware that validates the network connection, another that validates some specific feature params, etc. Anything you can image in order to prevent the execution of the retrofit call.
- Easy to inject — We need to create an object that can be easy to inject in our safe retrofit call wrapper extension.
- Easy to mock — We need somehow to make this middleware easy to mock in order to prevent our unit tests become hard to test.
- Retrieve all — We need a way to retrieve all our middlewares and validate each one of them.
Code time 🔥
Now that we set up the requirements for the Middleware, we can code. Let’s start with the first part. We are planning to integrate this functionality to our call wrapper extension, therefore, it means that the failure of a middleware is going to return a Failure object. That it’s our start point.
class NetworkMiddlewareFailure(
val middleWareExceptionMessage: String,
) : Failure.CustomFailure()
Easy cake. Let’s move to the next steep. Our app can have multiple middleware objects, and any of them needs to be validated somehow, let’s use the inheritance principle in order to accomplish this step.
This class will do the job. Every middleware we want to create will going to extend this class and override the isValid()
method (here is the place to put our our logic) and the failure
variable which extend from our custom failure from above.
Let’s create a simple middleware then!
The class from above is responsible for tell us if we are connected to the internet or not with the help of the connectivityUtils
interface (the implementation class is somewhere hided).
If the isValid()
method return false, then we are going to use the NetworkMiddlewareFailure
with a custom message from our res/strings folder with the help of the resourceProvider
interface. As you can see, our implementation is very flexible and allow us to use whatever we want to create a middleware.
Ok, we can create a lot of middlewares, and now what…? We need some way to provide all our middlewares and run the isValid()
method on each of them 🤯!
Hey hey hey 👊💥! Don’t panic. We can solve this problem very easily with the dependency inversion principle. It is better for us to depend on abstractions rather than implementations. Let’s create an interface that would do what we want.
Now is the turn for the implementation. We are going to create a class that implements our MiddlewareProvider
interface and overrides that getAll()
method in order to return all of our middleware objects. We can get a little bit fancy here and do it with a builder pattern.
As you can see, the class above is going to add all the middlewares you want. You only need to provide this class in your dependency injection graph which can be from dagger or koin or whatever.
Now we have a singleton instance of our middleware provider. Let’s modify the call wrapper extension!
Well done my friend 👏! You have implemented a middleware to our extension and make it even safer! Now you can use it as I show you in the first part of these series.
I hope you like it. If you have any suggestions, improvements, complaints, jokes, etc. Add a comment, file and issue, generate a PR, etc! Stay safe!
See how this implementation is used on this project
See you later! 👋