Context:
I've an Android App that is using Retrofit2 with RxJava to handle the server-side responses. The server-side knows how to handle etags so the server can return the request responses as 200 (Success) or 304 (Not Modified). The thing is that we want to be able to handle those responses differently in the Subscriber#onNext(). For example we have a local database implemented with SQLlite, so when a server-side request returns 200 we want to store that response in our local database, but if the request response returns 304 we don't want to do it, because it's the same data that we already have stored in our database and it's a waste of time saving it again.
We're using a Fernando Cejas Clean Architecture as our App architecture.
Our Interactors are using Repositories, which are using the Retrofit layer to perform the server-side request and returns the data like rx.Observable<List<SomeModelClass>>. Our Interactors are also using DAOs that returns database data like rx.Observable<List<SomeModelClass>>. So by doing this we can do some pretty cools stuff such as:
public void GetDogsInteractor {
...
public void execute (Callback callback) {
mDogsRepository.getAllDogs()
.onErrorNext(ObservableUtils.ifExceptionIs(SomeServerSideException.class, mDogsDao.getDatabaseDogs()))
.subscribe(
new Subscriber<List<Dog>>() {
...
public void onError() {
callback.showError("Sorry no doggos on server-side nor database");
}
public void onNext(List<Dog> dogs) {
callback.showDogsOnUI(dogs);
}
...
}
);
}
...
}
As you can see having the same interface for both our Repositories and our DAOs makes pretty easily plug one with another in case that something goes wrong.
The workaround:
Let's imagine that we want to store all the Dog objects in the database only if the request response is 200, but we don't if the request response is 304. The only solution that I could came with is filtering the server-side responses and throwing an Exception if the response is a 304, that way I can handle 200 request in the Subscriber#onNext() and 304s in the Subscriber#onError(). This is how the Repository internal implementation looks:
public class DogRepository {
...
public rx.Observable<List<Dog>> getAllDogs() {
mRetrofitService.getAllDogsFromServerSide() //Returns rx.Observable<Response<List<Dog>>>
.flatMap(new Func1<Response<List<Dog>>, Observable<List<Dog>>>() {
@Override
public Observable<List<Dog>> call(Response<List<Dog>> serverSideResponse) {
//Check the response code, if its 200 just pass the response through, if its 304 throw an exception
if (serverSideResponse.getCode() == 304) {
return Observable.error(new NotModifiedException(serverSideResponse.getBody()));
} else {
return Observable.just(serverSideResponse.getBody());
}
}
})
}
...
}
Then I create a custom subscriber that overrides the Subscriber#onError() method:
public class MySubscriber<T> extends rx.Subscriber<T> {
...
public void onError(Throwable e) {
if (e instance of NotModifiedException) {
onNotModified(((NotModifiedException) e).getBody()); //onNotModified() is a hook method
}
}
public void onNotModified(T model) {
//Optional to implement
}
...
}
Then finally I use it this way:
public void GetDogsInteractor {
...
public void execute (Callback callback) {
mDogsRepository.getAllDogs()
.onErrorNext(ObservableUtils.ifExceptionIs(SomeServerSideException.class, mDogsDao.getDatabaseDogs()))
.subscribe(
new MySubscriber<List<Dog>>() {
...
public void onError() {
callback.showError("Sorry no doggos on server-side nor database");
}
public void onNotModified(List<Dog> dogs) {
callback.showDogsOnUI(dogs);
}
public void onNext(List<Dog> dogs) {
mDogDao.saveAllDogs(dogs)
callback.showDogsOnUI(dogs);
}
...
}
);
}
...
}
This way I can even hook some other stream with the NotModifiedException using onErrorNext().
The problem:
Some of my coworkers are not quite happy of handling this throwing exceptions and handling the 304 responses as somethign "unexpected". Can't blame them, this is not the nicest solution. But I tried many things and this is the only approach that worked. My question is; Is there some way of achieving this without using Exceptions? Some cleaner way?
I simplify this as much as I could, but let me know if there's something that you don't get.