2

I'm on my way to make an MVVM example project without the complexity of injection dependency library and RX ( because I think it's better to understand how it works fundamentally for people without all this very efficient stuff ) but its harder to make :)

I'm in trouble, I use the CatApi here: https://thecatapi.com/ I'm trying to do a spinner that contains breeds name and also a picture of a cat to the left ( for each breed ) but in this API you can only get this result in two calls ( one for breeds, one for images for a breed ), I don't push the research on the API far because even if the API can solve my problem, I want to face the problem because it can happen later in my life :)

So there is my probleme i've made the following code :

BreedEntity :

package com.example.mvvm_kitty.data.local.entities

//Entity was used to be stored into a local DB so no use here
data class BreedEntity (

    val adaptability: Int,

    val affection_level: Int,

    val description: String,

    val id: String,

    var name: String,

    val life_span: String,

    val origin: String,

    var iconImage : BreedImageEntity?,

    var images: List<BreedImageEntity>?

){

}

the call into the BreedActivity :

  private fun subscribeToModel(breedsViewModel: BreedsViewModel) {

        //Todo: Gerer les erreurs reseau

        breedsViewModel.getBreeds().observe(this, Observer {

            breedEntities ->

            mBinding.catSelected = breedEntities[0]

            breedSpinnerAdapter = BreedsSpinnerAdapter(this, breedEntities)
            mBinding.breedSelector.adapter = breedSpinnerAdapter

            breedEntities.forEach {breedEntity ->
                breedsViewModel.getBreedImages(breedEntities.indexOf(breedEntity)).observe(this, Observer {
                    breedEntity.iconImage = it[0]
                })
            }


        })

    }

yeah I think made a foreach it's very dirty ( and also it doesn't work because don't run on the same time so when I set the images in the observer the "it" value is on the last item

there is my BreedsViewModel :

package com.example.mvvm_kitty.viewmodels

import android.app.Application
import android.util.Log
import android.view.animation.Transformation
import androidx.lifecycle.*
import com.example.mvvm_kitty.BasicApp
import com.example.mvvm_kitty.data.local.entities.BreedEntity
import com.example.mvvm_kitty.data.local.entities.BreedImageEntity
import com.example.mvvm_kitty.data.repositories.CatRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

class BreedsViewModel(application: Application, private val catRepository: CatRepository) : AndroidViewModel(application) {

    private val mObservableBreeds: LiveData<List<BreedEntity>> = catRepository.getBreeds()

    /**
     * Expose the product to allow the UI to observe it
     */
    fun getBreeds(): LiveData<List<BreedEntity>> {
        return mObservableBreeds
    }

    fun getBreedImages(index : Int): LiveData<List<BreedImageEntity>> {
        val breed = mObservableBreeds.value?.get(index)
        return catRepository.getBreedImages(breed!!.id)
    }

    /**
      * Factory is used to inject dynamically all dependency to the viewModel like reposiroty, or id
     * or whatever
     */
    class Factory(private val mApplication: Application) :
        ViewModelProvider.NewInstanceFactory() {

        private val mRepository: CatRepository = (mApplication as BasicApp).getCatRepository()

        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return BreedsViewModel(mApplication, mRepository) as T
        }
    }

}

and to finish the CatRepository method to get the images :


    private fun getBreedImagesFromApi(id: String) : LiveData<List<BreedImageEntity>>{

            mObservableBreedImages.addSource(catService.getAllImages(id, 10)){

                mObservableBreedImages.postValue(it.resource?.map { breedDto ->
                    breedDto.toEntity()})

            }


        return mObservableBreedImages
    }

My problem is the following how can I get my images for each item in a clean way ( because I think my code is good but the foreach observer part is very dirty )

If someone can help me it would be very nice :D, Thanks in advance for your time.

Abhishek Duppati
  • 610
  • 6
  • 18
Happeal
  • 21
  • 1
  • 3
  • Almost sure a switchMap transformation will help. I don't have time now to write an example, though. Check it here https://developer.android.com/reference/android/arch/lifecycle/Transformations#switchmap It should be something like Transformations.switchMap(catRepository.getBreeds) { // Here call the method to get images with each breed and create the Breeds entities with images } – Lenin Apr 25 '20 at 20:50
  • Don't use nested Observers, use `Transformations.switchMap` – EpicPandaForce Apr 26 '20 at 03:28

2 Answers2

0

Seems like instead of fetching the data separately, you should be fetching them at the same time and combining the result into one response.

Generally speaking:

  1. Remove getBreedImagesFromApi.
  2. Update your getBreedsFromApi (I assume that exists and you're using coroutines) to fetch both pieces of data in the one call you already have for getting breeds. You can use async and await() for this to fire off two requests to the two different endpoints, have them run concurrently, and wait for both requests to finish.
  3. Remove the "foreach" because the images will now exist by the time you get the list of breeds.

Hope that helps!

dominicoder
  • 9,338
  • 1
  • 26
  • 32
  • Okay that's what I think to use in the first place so I will be try with coroutines :) And I'll be in touch Thanks – Happeal Apr 26 '20 at 18:25
0

I guess the view should receive a completed object data from your view-model, which mean you must do all this logic in view model.so

  1. hitting both APIs & observe them in view-model.
  2. create one more Live Data responsible for passing data to your View(Activity/Fragment)
  3. update the ViewLiveData from both APIs observers.

and to do this you will need Transformation or MediatorLiveData in your view-model.

also you can use Rxjava, check fetch every item on the list

Gg M
  • 386
  • 3
  • 6