Search code examples
androidkotlinandroid-livedatadagger-hilt

LiveData returns wrong Object


I added Hilt to my project and now LiveData returns wrong Object type. Maybe I made some wrong changes in my code. getAllCurrencies returns LiveData<Resource<Unit>> but it should LiveData<Resource<Currencies>>

ViewModel:

class SplashScreenViewModel @ViewModelInject constructor(
private val roomDatabaseRepository: RoomDatabaseRepository,
private val retrofitRepository: RetrofitRepository) : ViewModel() {

fun getAllCurrencies(mainCurrency: String) = liveData(Dispatchers.IO) {
       emit(Resource.loading(data = null))
        try {
            emit(Resource.success(data = retrofitRepository.getAllCurrencies(mainCurrency)))
        } catch (exception: Exception) {
            emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
        }
    }

Repository: (It returns good type)

class RetrofitRepository @Inject constructor(val currenciesApiHelper: CurrenciesApiHelper) {

suspend fun getAllCurrencies(mainCurrency: String) {
  currenciesApiHelper.getAllCurrencies(mainCurrency)
}

Solution

  • You should return currenciesApiHelper.getAllCurrencies(mainCurrency) in to Repository.


    (optional) Following is the way I do it with MVVM:

    I'll assume that you have Currency as a model declared somewhere already.

    Status

    sealed class Status<out T> {
        class Loading<out T> : Status<T>()
        data class Success<out T>(val data: T) : Status<T>()
        data class Failure<out T>(val exception: Exception) : Status<T>()
    }
    

    Fragment/Presentation Layer

    viewModel.fetchCurrencies(mainCurrency)
                .observe(viewLifecycleOwner, Observer { result ->
                    when (result) {
                        is Status.Loading<*> -> {
                            //display a ProgressBar or so
                        }
    
                        is Status.Success<*> -> {
                            //Status.Success<*> can also be Status.Success<ArrayList<Currency>>
                            //hide the ProgressBar
                            val currencies = result.data as ArrayList<Currency> 
                        }
    
                        is Status.Failure<*> -> {
                            //log the exception
                        }
                    }
                })
    

    ViewModel

    private val repo = Repository()
    
    @ExperimentalCoroutinesApi
        fun fetchCurrencies(mainCurrency: String): LiveData<Status<MutableList<Currency>>> =
            liveData(Dispatchers.IO) {
                emit(Status.Loading())
    
                try {
                    repo.getCurrencies(mainCurrency).collect {
                        emit(it)
                    }
    
                } catch (e: Exception) {
                    emit(Status.Failure(e))
                    Log.e("ERROR:", e.message!!)
                }
            }
    

    Repository (single datasource)

    Using Firestore instead here as I'm not 100% certainly sure about your way.

    Do what retrofitRepository.getAllCurrencies(mainCurrency) should do and then offer the result.

    private val fs: FirebaseFirestore = Firebase.firestore
    
    @ExperimentalCoroutinesApi
        fun getCurrencies(mainCurrency: String): Flow<Status<MutableList<Currency>>> = callbackFlow {
    
            val subscription = fs.collection("currencies")
                .addSnapshotListener { snapshot, _ ->
                    if (snapshot != null) {
                        val result = snapshot.toObjects(Currency::class.java)
                        offer(Success(result))
                    }
                }
    
            awaitClose { subscription.remove() }
        }
    

    Using coroutines is by the way quite sweet. Have a look here:

    Learn advanced coroutines with Kotlin Flow and LiveData

    Android Coroutines: How to manage async tasks in Kotlin

    Hope it helps :)