I have a problem and I don't know how to resolve it with a better aproach. The problem is that I'm requesting to Spotify Web API and in some methods the artist image is returned and in others only basic artist info is obtained.
I have this two methods:
fun getAlbum(albumId: String): Single<SpotifyAlbumDTO>
fun getArtist(artistId: String): Single<SpotifyArtistDTO>
When I get an album, the artist info doesn't contains the artist image url. So, I need to call the getAlbum() method and use the result to obtain the artistId and then call to getArtist() method.
I have the following method to do all this stuff:
fun getAlbum(albumId: String): Single<Album>
On this method I need to call the previous two to return an Album object (my domain object). The only solution that worked for me it's the following:
fun getAlbum(albumId: String): Single<Album> {
return Single.create { emitter ->
_spotifyService.getAlbum(albumId).subscribe { spotifyAlbum ->
_spotifyService.getArtist(spotifyAlbum.artist.id).subscribe { spotifyArtist ->
val artistImage = spotifyArtist.imageUrl
spotifyAlbum.artist.image = artistImage
emitter.onNext(spotifyAlbum.toAlbum())
}
}
}
}
I think that must exist another better way to do this than concating subscribe calls in other subscribes and creating deeper calls. I also try the following:
_spotifyService.getAlbum(albumId).flatMap { spotifyAlbum ->
_spotifyService.getArtist(spotifyAlbum.artist.id)
}.flatMap { spotifyArtist ->
// Here I don't have the album and I can't to asign the image
}
To combine several source of data the best operator is zip:
Single.zip(getAlbum(albumId), getArtist(artistId),
BiFunction<SpotifyAlbumDTO, SpotifyArtistDTO, SpotifyAlbumDTO> { album, artist ->
val artistImage = artist.imageUrl
album.artist.image = artistImage
album //return value of the merged observable
}
).subscribe { album: SpotifyAlbumDTO?, error: Throwable? ->
emitter.onNext(album.toAlbum())
}
It will run all the observables in parallel, and execute the merging function once every observable has finished.
If you have more observable to zip, you can use Function3, Function4,...
If parallel execution is not possible because you need the request to be executed sequentially then, you can you the resultSelector of the flatmap function. It takes the item before the flatMap and the grouped collection after the flatmap. This way you will easily be able to create your model group, without any confusing usage of Pair
The only catch is: Single
does not support this kind of flatmap.
You can workaround the issue either changing your return type from Single
to Observable
or just convert your Single
at runtime with the toObservable
operator.
getAlbum(albumId)
.toObservable()
.flatMap({album: SpotifyAlbumDTO -> getArtist(album.artist.id).toObservable()},
{ album:SpotifyAlbumDTO, artist:SpotifyArtistDTO ->
album.artist.image = artist.imageUrl
album
})
}?.subscribe { album: SpotifyAlbumDTO ->
print(album)
}