Hi, is there a way to pass @launch to another func...
# coroutines
l
Hi, is there a way to pass @launch to another function? I have this piece
Copy code
viewModelScope.launch { 
    validateEmail(user)?.let {
        _errorLiveData.postValue(it)
        stopLoading()
        return@launch
    }
}
And I want it to look something like
Copy code
evaluate(validateEmail(user), launch)
with
Copy code
fun evaluate(result: String?, job: (
    context: CoroutineContext,
    start: CoroutineStart,
    block: suspend CoroutineScope.() -> Unit
) -> Job){
    result?.let {
        _errorLiveData.postValue(it)
        stopLoading()
        return@job
    }
}
I get job is unresolved, so what am I doing wrong and how to make it right?
w
Can you clarify what you want to achieve? I don’t quite understand from the snippets you pasted
l
I have a number of blocks like
Copy code
validateIpData(it)?.let {
    _errorLiveData.postValue(it)
    return@launch
}
Inside viewmodelScope.launch:
Copy code
viewModelScope.launch {
    // if cloud login, validate username as email
    validateEmail(user)?.let {
        _errorLiveData.postValue(it)
        stopLoading()
        return@launch
    }
    
    loginModel.login(user)?.let { it ->
        _errorLiveData.postValue(it)
        return@launch
    }
    // if success, move to device list view
    _navigateToDeviceList.postValue(username.value)
}
And I want to make it more readable
w
I don’t have any idea right now 😕 But I’d suggest avoiding multiple returns and non-local returns and either returning
Boolean
from your validation functions or e.g. throwing an exception if validation fails. Keeping it simple would probably help readability more than a fancy evaluate function which takes a while to understand in couple of months 🙂
m
i dont quite get it. Why would you want to return the launch block? I dont think that is what you want
basically from what i understand, if validateEmail is not null, and login is not null, you want to display an error. I think that design could be improved a bit 😛 Generally, it’s the other way around
personnally, for you usecase, i find plain old java more readable:
viewModelScope.launch { val emailError = validateEmail(user) if(emailError != null){ _errorLiveData.postValue(it) stopLoading() return@launch } val loginError = loginModel.login(user) if(loginError != null){ _errorLiveData.postValue(it) return@launch } _navigateToDeviceList.postValue(username.value) }
because the original code is a bit confusing to read. Yes, it is less code, but less readable as well
if you cannot change the design, just extract the code inside launch to a suspending function, and there you can just return
viewModelScope.launch { if(login()){ _navigateToDeviceList.postValue(username.value) } } suspend fun login(): Boolean validateEmail(user)?.let { _errorLiveData.postValue(it) stopLoading() return false } loginModel.login(user)?.let { it -> _errorLiveData.postValue(it) return false } }
something like this!
l
There is still a number of blocks
Copy code
{
        _errorLiveData.postValue(it)
        stopLoading()
        return@launch
    }
And there are actually more validations, which I wanted to make less pronounced, so that it basically reads like { validateEmail() validateUsername() validateIp() ... }
Using suspend function looks like a good idea, I'll try it out, thanks!
m
how about this
viewModelScope.launch { val loginError = login() if(loginError == null){ _navigateToDeviceList.postValue(username.value) } else{ _errorLiveData.postValue(loginError) stopLoading() } } suspend fun login(): Error? return validateEmail(user) ?: loginModel.login(user) ?: null }
or this
viewModelScope.launch { login()?.let{ _errorLiveData.postValue(loginError) stopLoading() } ?: _navigateToDeviceList.postValue(username.value) } suspend fun login(): Error return validateEmail(user) ?: loginModel.login(user) ?: null }
but less readable imo. It’s a matter of preference