I am trying to map API errors (exceptions) to String resources in my view model. My view model looks like the following.
@HiltViewModel
class AccountViewModel @Inject constructor(accountRepository: AccountRepository) : ViewModel() {
val isLoadingProfile = MutableLiveData(false)
val profile = MutableLiveData<Profile>()
val profileLoadError = MutableLiveData<Int>()
fun loadProfile() {
isLoadingProfile.postValue(true)
viewModelScope.launch(Dispatchers.IO) {
try {
profile.postValue(accountRepository.getProfile())
} catch (e: Throwable) {
// assign String resource id to profileLoadError
profileLoadError.postValue(
when (e) {
is NetworkError -> R.string.network_error
else -> R.string.unknown_error
}
)
} finally {
isLoadingProfile.postValue(false)
}
}
}
}
And my error text view XML looks like the following.
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{viewModel.profileLoadError ?? ``}"
android:visibility="@{viewModel.profileLoadError != null ? View.VISIBLE : View.GONE}" />
But I am getting a class cast exception when I try to run it.
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.CharSequence
If I remove the null
coalescing operator from the data binding expression like the following, I get a resource not found exception.
android:text="@{viewModel.profileLoadError}"
Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x0
I know that it is kind of an idiotic question, but how do I fix this?
After the suggestion from @androidLearner, I created a binding adapter for the android:text
attribute.
@BindingAdapter("android:text")
fun TextView.setText(@StringRes resId: Int?) {
resId ?: return
if (resId == ResourcesCompat.ID_NULL) {
text = ""
} else {
setText(resId)
}
}
Now, I can pass the string id without worrying about its nullability.
android:text="@{viewModel.profileLoadError}"