Arsal
Arsal

Reputation: 191

How to return value from async coroutine scope such as ViewModelScope to your UI?

I'm trying to retrieve a single entry from the Database and successfully getting the value back in my View Model with the help of viewModelScope, but I want this value to be returned back to the calling function which resides in the fragment so it can be displayed on a TextView.

I tried to return the value the conventional way but it didn't work. So, How Can I return this value from viewModelScope.launch to the calling function?

View Model

fun findbyID(id: Int) {
    viewModelScope.launch {
        val returnedrepo = repo.delete(id)
        Log.e(TAG, returnedrepo.toString())
        // how to return value from here to Fragment
    }
}

Repository

suspend fun findbyID(id: Int): userentity {
    val returneddao = Dao.findbyID(id)
    Log.e(TAG, returneddao.toString())
    return returneddao
}

Upvotes: 15

Views: 22115

Answers (3)

Sarthak_ssg5
Sarthak_ssg5

Reputation: 108

Another way without using LiveData would be like this,

Similar to viewModelScope there is also a lifecycleScope available with lifecycle-aware components, which can be used from the UI layer. Following is the example,

Fragment

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    findByID.setOnClickListener {
        lifecycleScope.launch{
            val res = usermodel.findbyID(et2.text.toString().toInt())
            // use returned value to do anything.
        }
    }
}

ViewModel

//1st option
// make the function suspendable itself.use aync instead of launch and then
// use await to collect the returned value.
suspend fun findbyID(id: Int): userEntity  {
    val job = viewModelScope.async {
       val returnedrepo = repo.delete(id)
       Log.e(TAG,returnedrepo.toString())
       return@async returnedrepo
    }
    return job.await()
}

//2nd option
// make the function suspendable itself. but switch the execution on IO
// thread.(since you are making a DB call)
suspend fun findbyID(id: Int): userEntity  {
    return withContext(Dispatchers.IO){
       val returnedrepo = repo.delete(id)
       Log.e(TAG,returnedrepo.toString())
       return@withContext returnedrepo
    }
}

Since LiveData is specific to Android Environment, Using Kotlin Flow becomes a better option in some places, which offers similar functionality.

Upvotes: 4

Arsal
Arsal

Reputation: 191

Thank you Nataraj KR for your Help!

Following is the code that worked for me.

View Model

class ViewModel(application: Application):AndroidViewModel(application) {
val TAG = "ViewModel"
val repo: theRepository
val alldata:LiveData<List<userentity>>
val returnedVal = MutableLiveData<userentity>()
init {
    val getDao = UserRoomDatabase.getDatabase(application).userDao()
    repo = theRepository(getDao)
    alldata = repo.allUsers

}

fun findbyID(id: Int){
    viewModelScope.launch {
       returnedVal.value = repo.findbyID(id)
    }
}

}

Fragment

 override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    val usermodel = ViewModelProvider(this).get(ViewModel::class.java)
    usermodel.alldata.observe(this, Observer {
        Log.e(TAG,usermodel.alldata.value.toString())
    })
    usermodel.returnedVal.observe(this, Observer {
        tv1.text = usermodel.returnedVal.value.toString()
    })

    allData.setOnClickListener {
        tv1.text = usermodel.alldata.value.toString()
    }

    findByID.setOnClickListener {
        usermodel.findbyID(et2.text.toString().toInt())
    }
}

Upvotes: 1

Nataraj KR
Nataraj KR

Reputation: 1102

LiveData can be used to get value from ViewModel to Fragment.

Make the function findbyID return LiveData and observe it in the fragment.

Function in ViewModel

fun findbyID(id: Int): LiveData</*your data type*/> {
    val result = MutableLiveData</*your data type*/>()
    viewModelScope.launch {
       val returnedrepo = repo.delete(id)
       result.postValue(returnedrepo)
    }
    return result.
}

Observer in Fragment

findbyId.observer(viewLifeCycleOwner, Observer { returnedrepo ->
   /* logic to set the textview */
})

Upvotes: 22

Related Questions