'Kotlin & flow & MVVM -> observer triggered multiple time when switch fragment

I'm learning flows and implementing them in my project, but I'm not sure I understood everything.

This is my model

data class ResultResponse (
    @field:Json(name = "count") val count : Int?,
    @field:Json(name = "favorites") val "favorites") : List<Favorite>?
)

This is my service

@GET("...")
suspend fun getFavorites(@Path("visitor") visitor: String) : Response<ApiModel<ResultResponse>>

This is my repository

suspend fun getFavorites(visitor: String) = flow {
    emit(apiCall { api.getFavorites(visitor) })
}.onStart {
    emit(State.loading())
}

Where apiCall is

suspend fun <T> apiCall(call: suspend () -> Response<T>): State<T>

This is my view model

private val parentJob = Job()

private val coroutineContext: CoroutineContext
    get() = parentJob + Dispatchers.Default

private val scope = CoroutineScope(coroutineContext)

private val repository = FavoriteRepository(Api.favoriteService)

private val _favorites = MutableLiveData<State<ApiModel<ResultResponse>>>()

val favorites: LiveData<State<ApiModel<ResultResponse>>>
    get() = _favorites

fun fetchFavorites() {
    scope.launch {
        repository.getFavorites(Preferences.visitor).collect {
            _favorites.postValue(it)
        }
    }
}

This is my fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    observer()
}

override fun onResume() {
    super.onResume()

    favoriteViewModel.fetchFavorites()
}

private fun observer() {
    favoriteViewModel.favorites.observe(viewLifecycleOwner) { state ->
        when (state) {
            is State.Loading -> doSomethingOnLoadingState()
            is State.Success -> doSomethingOnSuccessState(state)
            is State.Error -> doSomethingOnErrorState(state)
        }
    }
}

The issue is when I switch fragment and come back to this one, it observes again the last state, so I got State.Success then State.Loading then State.Success triggered. I tried to solve it using an Event and getContentIfNotHandled()? but it didn't change anything.

And the second question is did I do it right, is this the best way to do it currently?



Solution 1:[1]

Everything works as expected. After returning to the fragment, your LiveData still holds previous Success state, then gets another Loading and Success from repository.

For me, having a loading state in your repository doesn't seem right. I'd move it to ViewModel and then emit it only if necessary(if you actually want to show it in view), e.g. by checking current liveData state or by having a boolean flag in fetchFavourites method.

As for the second question - as always - it depends. I, personally wouldn't create a flow for a single api call, but would rather use a suspend function.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 r2rek