CONTACT ME

  • Black Twitter Icon
  • Black Facebook Icon
  • Black LinkedIn Icon

Want to record an episode with me? Want to invite me to speak at your conference? let me know!

  • Britt Barak

Clean, Easy & New- How To Architect Your App: Part 3 — Network Calls


This is not exactly a direct continuation for the previous posts with the same title (part1, part2, which I’ll get back to soon hopefully ) , but really on the same subject 😉


Following an intro talk I gave recently, I want to show an example for structuring your app to perform network requests.


As an example, I created a simple app that finds some cool venues around a specific location, using Foursquare API and Retrofit for the network calls.



If you’ve read through previous posts, the outline should be pretty obvious:


Presentation layer, on button click will execute GetVenues use case, which will ask VenuesRepository for the venues’ data.


Repository object, as discussed before (in Part 2) is in charge of getting the data without exposing the logic of how or from where did it come from. In this example, to focus on the point, I’m making network requests only.

class VenuesRepository {
    //...
    AppApiClient client = AppApiClient.get();
   LiveData<VenuesResponse> getVenues(String location) {
        return client.getVenues(location);
    }
}

[more about LiveData, in Part 1: goo.gl/BJ1nHb ]



Who is this AppApiClient?


It is the one who knows all the api services we work with. If VenuesRepository chooses to get the data from the network (and not from a local cache or a local db, for instance), AppApiClient will turn to the correct service to get the data.


** There are more ways to implement it, (DI is an awesome way, for example. However, it requires more previous knowledge, so I decided to implement it here as simple as possible, and talk about DI on another opportunity).


class AppApiClient {
    //...

    LiveData<VenuesResponse> getVenues(String location) {
    final LiveData<VenuesResponse> data = new MutableLiveData<>();

    ServiceGenerator.getFoursquareService()
        .getVenues(location)
        .enqueue(new Callback<VenuesResponse>() {
            
            @Override
            public void onResponse(Call<VenuesResponse> call,     
                            Response<VenuesResponse> response) {
                data.setValue(response.body());
            }

       //...
    });
    
    return data;
    }
}

[*For simplicity, failure handling was omitted, and would be discussed another time.]


Assuming you have prior knowledge on Retrofit, the line we’re not familiar with is:

ServiceGenerator.getFoursquareService().getVenues()


What’s going on there?

ServiceGenerator is the class with all my network configurations.


I’m using Retrofit in this case so:

public class ServiceGenerator {

    static OkHttpClient.Builder httpClient = ...;

    static Retrofit.Builder retrofitBuilder = ...;
   static Retrofit retrofit = retrofitBuilder.build();

    static FoursquareService foursquareService =       retrofit.create(FoursquareService.class);

//...

}
  • Note: The members here are static, so that we only need to define them once for the entire app. Both for memory performance reasons, and so that we only need to make general configurations once.


FoursquareService is the interface I give Retrofit to create FoursquareAPi’s calls:

public interface FoursquareService {

    @GET("venues/explore")
    Call<VenuesResponse> getVenues(@Query("ll") String location);

    //...
}

So structure looks something like this:



Going back for a bit to ServiceGenerator.retrofitBuilder.


We all know and love Gson, Jackson and other converters. I’ll just point out real quick another useful converter we have to implement ourselves: EnvelopingConverter.


It has been talked about before in a few resources. I’ll just mention it in a few words though:


The responses on this specific api calls look something like that:

{
  "meta": {
    "code": ...,
    "requestId": ...
  },
  
  "response": {
    //...
  }
}


As I’m only interested in the “response” field of the response object, EnvelopingConverterpeels off the response object for me, so that the object that actually returns from the call will only be the value of the “response” field.

How to do that?


I created a data model for the response object. As said, I’m only interested in the “response” field, so this is the only field I kept in the data model. Since I’d like to reuse this object for any of the responses from this api, not only for getting the venues, I use a generic type:

class Envelope<T> {
    T response;
}

EnvelopingConverter uses Gson to convert the response json to Envelope class, so it can take only the response field. Then it asks the next converter that was added to Retrofit (retrofit.nextResponseBodyConverter) to handle only the envelope.response.


class EnvelopingConverter extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {

    Type envelopedType = 
         TypeToken.getParameterized(Envelope.class, type).getType();
    
    final Converter<ResponseBody, Envelope<?>> delegate =    
    retrofit.nextResponseBodyConverter(this, envelopedType, annotations);
    
    return new Converter<ResponseBody, Object>() {
        @Override
        public Object convert(ResponseBody body) throws IOException {
            Envelope<?> envelope = delegate.convert(body);
            
            return envelope.response;
        }
    };
}

Using in on ServiceGenerator, is something like:

static Retrofit.Builder retrofitBuilder =
        new Retrofit.Builder()
                .baseUrl(FOURSQUARE_BASE_URL)
                .client(httpClient.build())
                .addConverterFactory(new EnvelopingConverter())
                .addConverterFactory(GsonConverterFactory.create());


That way, Retrofit first peels off the response object that returns from the api, and then uses the GsonConverter for serialization of only envelope.response, since GsonConverter is the next converter we added after EnvelopingConverter.



Nice! So this is basically the structure of my network classes.



I want to point out here that Repository.getVenues() returns LiveData from Room’s local database. In other words, GetVenues use case always observes the database changes.



If Repository decides to go to the network and refresh the data, when new data returns it updates the local database only. Since the local database changes, notifications will be sent to all of the observers (in my case, Repository’s observers are always UseCases).


That way, we can always count on the database to be a single source of truth.


Also, if we decide to change or add the venues’ data source, for example adding a new api, there will be only a few changes to be made, only on the Repositories end.


The only thing needed, is for the Repository to convert the new data source’s data model to the database’s data model.


Since all the Usecases would work with the database directly, and the models saved in it won’t change, non of the Usecases will be effected.




There are a few more nice things to notice here on this implementation, will point them out in a short upcoming post.


Github demo project can be found here: github.com/brittBarak/NetworkingDemo

Thanks for reading ❤️🍓🎈