3

I'm using a Builder pattern to build upon a model object which combines data from different network calls and I'm having a hard time understanding the best way to take the model object from the first network call and combine the data from the second network call into the original model object.

My actual subscription:

myFirstApiRepository.getFirstModelObjectBuilder()
    .flatmap(firstModelObjectBuilder -> mySecondApiRepository.getSomeExtraData(firstModelObjectBuilder))
    .observable.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(getMySubscriber());

First network call:

public Observable<FirstModelObject.Builder> getFirstModelObjectBuilder() {
    return myFirstApiResource.getSomeData(...)
            .flatMap(someData -> Observable.just(new FirstModelObject.Builder()
                .setFirstAttribute(someData.getFirstAttribute())
                .setSecondAttribute(someData.getSecondAttribute())));
}

Second network call:

public Observable<FirstModelObject> getSomeExtraData(FirstModelObject.Builder builder) {
    return mySecondApiResource.getSomeData(...)
        .flatMap(aString -> builder.setSomeStringValue(aString)
                                   .build());
}

The problem here is that I have to pass the builder object into the second network call's observable. This makes it very rigid and I'd rather not have my SecondApiRepository rely on and reference a data type which it shouldn't need to reference. It also makes this second method here ".build()" the object, which is not good. So, how can I use the firstModelObject and add data to it from the second network call in a clean way?

If this is just bad design, please let me know. I'm still trying to learn more about RxJava best practices. :)

w3bshark
  • 2,700
  • 1
  • 30
  • 40

1 Answers1

1

If your second request relies on first request's result, then check my answer - https://stackoverflow.com/a/41820372/7045114.

If not - just use zip operator:

Combines the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function.

Observable.zip(firstRequest, secondRequest, (firstResult, secondResult) -> {
    //process results
})
Community
  • 1
  • 1
Maksim Ostrovidov
  • 10,720
  • 8
  • 42
  • 57
  • In my example, the first request returns an Observable of one type and the second request returns an Observable of another type. I think your linked answer only works when the two data types are the same. Please correct me if I'm wrong. – w3bshark Feb 07 '17 at 06:14
  • It doesn't depend on Observable type. FlatMap transforms Observables regardless of their type. – Maksim Ostrovidov Feb 07 '17 at 06:23
  • Look closely at functions declarations - e.g. flatMap needs `Func1 super T, ? extends Observable extends R>>` - first value - input type, second - output. Both can be completely different. – Maksim Ostrovidov Feb 07 '17 at 06:36
  • 1
    I see it now. I was incorrectly trying to return an Observable of my second type in the second function / result selector. I should've just returned my second type. Thank you very much for the help! – w3bshark Feb 07 '17 at 06:41
  • `zip` I feel is the appropriate way here - `mySecondApiResource.getSomeData(...)` would be the second `Observable` to the `zip` and `Function` would perform `builder.setSomeStringValue(aString).build()` – Pavan Kumar Feb 07 '17 at 07:59
  • I would use `zip`, except the second network call relies on information from the first network call. – w3bshark Feb 07 '17 at 14:42