DevPeter
DevPeter

Reputation: 81

Is it correct to use suspend fun that returns with context value inside another suspend fun that returns with context value?

At the moment I am using in my app suspend fun such as getSessionId(), getLocationLatitude(), getLocationLongitude(), getActionDateTime() and getUserName() which returns some values from the database using return withContext(ioDispatcher).

Then, I am calling these functions in my private fun testViewModelScope() inside the viewModelScope.launch() { to get these values ready for use. However, I released that instantiating/getting all these values inside the private fun testViewModelScope() grows the line of code rapidly and makes the code less readable.

For that reason, I created another suspend fun getMainActivityDataClassObject() that return@withContext data class that includes all the information need it. All values are called from the suspend fun such as getSessionId() and others. The function is used in the private fun testViewModelScope() inside the viewModelScope.launch() {.

I am trying to improve my knowledge about the main-safe use, return@withContext and viewModelScope.launch().

My first question is:

Is it correct to use suspend fun with return@withContext inside another suspend fun with return@withContext?

My second question is:

Is it anything in my code that I can improve?

My third question is:

Is it any better way to do it?

Repository:

override suspend fun getSessionId(): Int? {
    return withContext(ioDispatcher) {
        firstDao.getSessionId()
    }
}


override suspend fun getLocationLatitude(): String? {
    return withContext(ioDispatcher) {
        firstDao.getLocationLatitude()
    }
}

override suspend fun getLocationLongitude(): String? {
    return withContext(ioDispatcher) {
        firstDao.getLocationLongitude()
    }
}

override suspend fun getActionDateTime(): String? {
    return withContext(ioDispatcher) {
        firstDao.getActionDateTime()
    }
}

override suspend fun getUserName(): String? {
    return withContext(ioDispatcher) {
        firstDao.getUserName()
    }
}

override suspend fun getMainActivityDataClassObject(): MainActivityDataObjectClass = withContext(ioDispatcher) {
    val sessionId = getSessionId()
    val latitude = getLocationLatitude()
    val longitude = getLocationLongitude()
    val actionDateTime = getActionDateTime()
    val userName = getUserName()
    val dataObject = MainActivityDataObjectClass(
        sessionId,latitude,longitude,actionDateTime,userName
    )
   return@withContext dataObject
}

Data Class

@Parcelize
data class MainActivityDataObjectClass(
    var sessionId: Int?,
    var latitude: String?,
    var longitude: String?,
    var actionDateTime: String?,
    var userName: String?,
) : Parcelable

ViewModel:

private fun testViewModelScope() {
    viewModelScope.launch() {

        try {

            // TODO APPROACH NO 1
            val sessionId = mainActivityRepository.getSessionId()
            val latitude = mainActivityRepository.getLocationLatitude()
            val longitude = mainActivityRepository.getLocationLongitude()
            val actionDateTime = mainActivityRepository.getActionDateTime()
            val userName = mainActivityRepository.getUserName()

            // TODO APPROACH NO 2
            val dataObject = mainActivityRepository.getMainActivityDataClassObject()
            
        } catch (e: CancellationException) {
            throw e
        } catch (ex: Exception) {
        }
    }
}

Upvotes: 0

Views: 1019

Answers (1)

Tenfour04
Tenfour04

Reputation: 93834

Question 1: It is unnecessary to use withContext to call a suspend function. Using withContext(Dispatchers.IO) is for calling blocking code. Suspend functions by convention must not block. None of the suspend functions from Google, Jetpack, Square, Kotlinx, etc. libraries block. This includes Room DAO functions that you mark with the suspend keyword.

Question 2 and 3:
Approach 2 versus 3 is just a matter opinion and you don’t show what you do with your code next so it’s hard to say.

It’s usually wrong to do a blanket catch of all Exceptions like you are doing. You should be responding to expected exceptions like IOException somewhere high up so you can inform the user or recover.

Catching and rethrowing CancellationExceptions can be a bit of a gotcha. I think it’s ok here, but there are APIs (especially ones defined in Java) that can throw them for reasons other than cancelling a coroutine. Just beware for that possibility.

Upvotes: 1

Related Questions