简体   繁体   中英

How to observe Repository LiveData from Fragment via ViewModel

I am having a hard time figuring out how I can connect my Repository and ViewModel 's live data in-case of @GET request and observe them in the fragment.

I don't have this problem when the request type is @POST because I can use Transformation.switchMap on the body and whenever the body changes repository's function gets invoked and emits value to the response live data something like this

val matchSetsDetail: LiveData<Resource<MatchDetailBean>> = Transformations.switchMap(matchIdLiveData) { matchId ->
        val body = MatchSetRequest(matchId)
        repository.getMatchSet(body)
    }

but in case of @GET request, I have several query parameter that my View supplies

I have this retrofit API call in repository class and the code looks like this

class Repository {
    fun checkInCheckOutUser(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long): LiveData<Resource<BaseResponse>> = liveData {
            emit(Resource.Loading())
            try {
                val response: Response<BaseResponse> = ApiClient.coachApi.checkInCheckOutUser(apiKey, userId, status, latitude, longitude, checkedOn)
                if (response.isSuccessful && response.body() != null) {
                    if (response.body()!!.isValidKey && response.body()!!.success) {
                        emit(Resource.Success(response.body()!!))
                    } else {
                        emit(Resource.Failure(response.body()!!.message))
                    }
                } else {
                    emit(Resource.Failure())
                }
            } catch (e: Exception) {
                emit(Resource.Failure())
            }
        }
}

and ViewModel

class CheckInMapViewModel : ViewModel() {
    val checkInResponse: LiveData<Resource<BaseResponse>> = MutableLiveData()

        fun checkInCheckOut(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long): LiveData<Resource<BaseResponse>> {
            return repository.checkInCheckOutUser(apiKey,userId,status,latitude,longitude,checkedOn)
        }
    }

The main problem is I want to observe checkInResponse the same way I am observing in case of @POST request but don't know how to pass observe repository LiveData as I did with my post request above using Transformations.switchMap . Can anyone help me with this case?

Edit - Here is my retrofit service class as asked

interface CoachApi {
    @POST(Urls.CHECK_IN_CHECK_OUT_URL)
    suspend fun checkInCheckOutUser(
        @Query("apiKey") apiKey: String,
        @Query("userId") userId: Int,
        @Query("status") status: String,
        @Query("latitude") latitude: Double,
        @Query("longitude") longitude: Double,
        @Query("checkedOn") checkedOn: Long
    ): Response<SelfCheckResponse>

    @POST(Urls.SELF_CHECK_STATUS)
    suspend fun getCheckInStatus(
        @Query("apiKey") apiKey: String,
        @Query("userId") userId: Int
    ): Response<SelfCheckStatusResponse>
}

The Transformations.switchMap() just utilizes MediatorLiveData . Since your use-case is a bit different, you could just directly implement it yourself.

class CheckInMapViewModel : ViewModel() {
    private val _checkInResponse = MediatorLiveData<Resource<BaseResponse>>
    val checkInResponse: LiveData<Resource<BaseResponse>> = _checkInResponse

    fun checkInCheckOut(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long) {
        val data = repository.checkInCheckOutUser(apiKey,userId,status,latitude,longitude,checkedOn)
        _checkInResponse.addSource(data) {
            if (it is Resource.Success || it is Resource.Failure)
                _checkInResponse.removeSource(data)
            _checkInResponse.value = it
        }
    }
}

This code is assuming that data only emits one terminal element Resource.Success or Resource.Failure and cleans up the source with it.

Your desirable approach is possible using an intermediate LiveData which holds the request params, named queryLiveData . When the checkInCheckOut function is called, we set a new value for it that causes a change in checkInResponse . Then the change will be transformed into the result of repository.checkInCheckOutUser using switchMap .

CheckInMapViewModel:

class CheckInMapViewModel : ViewModel() {

    private val queryLiveData = MutableLiveData<CheckInCheckOutParam?>()

    init {
        queryLiveData.postValue(null)
    }

    val checkInResponse: LiveData<Resource<BaseResponse>> =
        queryLiveData.switchMap { query ->
            if(query == null) {
                AbsentLiveData.create()
            } else {
                repository.checkInCheckOutUser(
                    query.apiKey,
                    query.userId,
                    query.status,
                    query.latitude,
                    query.longitude,
                    query.checkedOn
                )
            }
        }

    fun checkInCheckOut(
        apiKey: String,
        userId: Int,
        status: String,
        latitude: Double,
        longitude: Double,
        checkedOn: Long
    ) {
        queryLiveData.postValue(
            CheckInCheckOutParam(apiKey, userId, status, latitude, longitude, checkedOn)
        )
    }

    private data class CheckInCheckOutParam(
        val apiKey: String,
        val userId: Int,
        val status: String,
        val latitude: Double,
        val longitude: Double,
        val checkedOn: Long
    )
}

AbsentLiveData:

/**
 * A LiveData class that has `null` value.
 */
class AbsentLiveData<T : Any?> private constructor(resource: Resource<T>) :
    LiveData<Resource<T>>() {

    init {
        // use post instead of set since this can be created on any thread
        postValue(resource)
    }

    companion object {

        fun <T> create(): LiveData<Resource<T>> {
            return AbsentLiveData(Resource.empty())
        }
    }
}

Try this:

class CheckInMapViewModel : ViewModel() {
    private val _checkInResponse: MediatorLiveData<Resource<BaseResponse>> = MediatorLiveData()
    val checkInResponse: LiveData<Resource<BaseResponse>>
    get() = _checkInResponse

    init {
        _checkInResponse.addSource(checkInCheckOut()) {
            _checkInResponse.value = it
        }
    }

    fun checkInCheckOut(apiKey: String, userId: Int, status: String, latitude: Double, longitude: Double, checkedOn: Long): LiveData<Resource<BaseResponse>> {
        return repository.checkInCheckOutUser(apiKey,userId,status,latitude,longitude,checkedOn)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM