Search code examples
kotlinretrofitrx-java2

How to combine several API calls to create single response object using RxJava


I'm building an application which consists of two API calls. I'm still learning RxJava and I'm not sure how to combine properly the two API calls

  1. The first API call is used to retrieve items in form of a list
  2. The second API call is used to retrieve item image using the item name that I got from the first call.

I need to show all the items with their images. Those are my API calls using retrofit

@GET("items/list")
fun getItems(): Observable<ItemResult>

@GET("item/{name}/images")
fun getItemDetails(@Path("name") name: String): Observable<ItemDetails>

This is the code that wrote with RxJava:

    fun getItemsData(): Observable<ArrayList<ItemDetails>> {

    val data = ArrayList<ItemDetails>()
getItems().flatMap { itemResponse -> Observable.just(itemResponse.message) } //this will give me a list with item names
        .flatMapIterable { data -> data }//iterating over the list and for every item...
        .map { itemName ->//calling to get the item image
            getItemDetails(itemName).map { imageData ->
                val itemImage = imageData.message
                data.add(ItemData(itemName, itemImage))//from this point on I'm lost, I'm not sure if it's the right thing to add here the data
            }.subscribeOn(Schedulers.io())
        }.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())

    return Observable.create(data)// not sure how to create an Observable from the data
}   

Solution

  • You don't really need this...

    val data = ArrayList<ItemDetails>()
    

    flatMap is not the right operator here:

    flatMap { itemResponse -> Observable.just(itemResponse.message) }
    

    you can simplify this by using map instead (the function itemResponse -> itemResponse.message operates on the inner value only).

    map { itemResponse -> itemResponse.message }
    

    Next:

    .flatMapIterable { data -> data } // here we have Observable<Message>
    .flatMap { itemName ->
         getItemDetails(itemName).map { imageData ->
            ItemData(itemName, imageData)
         }
     } // Observable<ItemData>
    .toList() // Single<List<ItemData>> -> you can use toObservable to get an Observable<List<ItemData>>
    

    You can use toList() instead of manually creating and populating the ArrayList, it simplifies things.