Question: How can I update my UI after a coroutine is completed in Kotlin for Android?

I use Retrofit and Coroutines to fetch a list of languages from an API. My ViewModel loads the list of languages whenever it is created. I want to load this list into a spinner, but whenever I update the spinner data, the data in the ViewModel hasn't completely loaded.
Here my viewmodel class to load data from an api
class TranslateViewModel : ViewModel() {
    var langToCode = HashMap<String, String>()
    var codeToLang = HashMap<String, String>()
    private val translateRepository = TranslateRepository()

    init {
        viewModelScope.launch {
            val langList = try {
                translateRepository.getLangList()
            } catch (e: IOException) {
                Log.e(TAG, "IOException", e)
                return@launch
            } catch (e: HttpException) {
                Log.e(TAG, "HTTPException", e)
                return@launch
            }
            if (langList.isSuccessful && langList.body() != null) {
                codeToLang = langList.body()!!.langList
                langToCode = codeToLang.map { (key, value) ->
                    value to key
                }.toMap() as HashMap<String, String>
            } else {
                Log.d(TAG, "Get lang list failure")
            }
        }
    }
}

I want to load my list of languages into my spinner but my viewmodel is taking too long to load the data, so the langList variable is alway empty.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewBinding.apply {
            sourceLanguage.apply {
                val langList = viewModel.langToCode.keys.toList()
                val arrayAdapter = ArrayAdapter (
                    context,
                    android.R.layout.simple_spinner_item,
                    langList
                ).also { it.setDropDownViewResource(
                     android.R.layout.simple_spinner_dropdown_item
                )
                }
                this.adapter = arrayAdapter
        }
}

I want to know how to update my UI immediately after my coroutine has finished loading data. I appreciate all of your help, and please forgive me for my poor English skills.


Answer:
In Your case update your viewmodel as below-
class TranslateViewModel : ViewModel() {
    private val _langList = MutableLiveData<List<String>>()
    val langList: LiveData<List<String>> get() = _langList
    
    init {
        viewModelScope.launch {
            if (langList.isSuccessful && langList.body() != null) {
                codeToLang = langList.body()!!.langList
                langToCode = codeToLang.map { (key, value) ->
                    value to key
                }.toMap() as HashMap<String, String>
                _langList.value = langToCode.keys.toList() // Update the LiveData value
            } else {
                Log.d(TAG, "Get lang list failure")
            }
        }
    }
}

In your UI code to observe the LiveData and update the spinner as below-
viewModel.langList.observe(viewLifecycleOwner, { langList ->
            val arrayAdapter = ArrayAdapter(
                context,
                android.R.layout.simple_spinner_item,
                langList
            ).also { it.setDropDownViewResource(
                 android.R.layout.simple_spinner_dropdown_item
            )
            }
            sourceLanguage.adapter = arrayAdapter
        })

Follow this link for more detail - https://developer.android.com/topic/libraries/architecture/livedata