I have this sealed class:
sealed class Resource<out T> {
object Loading: Resource<Nothing>()
data class Success<out T>(val data: T): Resource<T>()
data class Failure(val message: String): Resource<Nothing>()
}
In the repository class I have this function that deletes an item from an API:
override suspend fun deleteItem(id: String) = flow {
try {
emit(Resource.Loading)
emit(Resource.Success(itemsRef.document(id).delete().await()))
} catch (e: Exception) {
emit(Resource.Failure(e.message))
}
}
The result of the delete operation is Void?
. Now, in the ViewModel class I declare:
val state = mutableStateOf<Resource<Void?>>(Success(null))
And update it when the delete completes:
fun deleteItem(id: String) {
viewModelScope.launch {
repo.deleteItem(id).collect { response ->
state.value = response
}
}
}
I have created a Card and inside onClick I have added:
IconButton(
onClick = viewModel.deleteItem(id),
)
Which actually deletes that item form database correctly. But I cannot track the result of the operation. I tried using:
when(val res = viewModel.state.value) {
is Resource.Loading -> Log.d(TAG, "Loading")
is Resource.Success -> Log.d(TAG, "Success")
is Resource.Failure -> Log.d(TAG, "Failure")
}
But only the case Loading is triggered. No success/failure at all. What can be wrong here? As it really acts like a synchronous operation.
CodePudding user response:
I've tested your approach without a repository, and compose part looks totally fine:
var i = 0
@Composable
fun TestScreen(viewModel: TestViewModel = viewModel()) {
val state by viewModel.state
Text(
when (val stateSmartCast = state) {
is Resource.Failure -> "Failure ${stateSmartCast.message}"
Resource.Loading -> "Loading"
is Resource.Success -> "Success ${stateSmartCast.data}"
}
)
Button(onClick = {
viewModel.deleteItem( i)
}) {
}
}
class TestViewModel : ViewModel() {
val state = mutableStateOf<Resource<Int>>(Resource.Success(i))
fun deleteItem(id: Int) {
viewModelScope.launch {
deleteItemInternal(id).collect { response ->
state.value = response
}
}
}
suspend fun deleteItemInternal(id: Int) = flow {
try {
emit(Resource.Loading)
delay(1000)
if (id % 3 == 0) {
throw IllegalStateException("error on third")
}
emit(Resource.Success(id))
} catch (e: Exception) {
emit(Resource.Failure(e.message ?: e.toString()))
}
}
}
So the the problem looks like in this line itemsRef.document(id).delete().await())
, or in your connection to the repository.
CodePudding user response:
Try collecting in the composable function:
val state = viewModel.state.collectAsState()
Then you can do: when (val res = viewModel.state.value){...}
.
However I am sceptical about the deleteItem
in the repository returning a flow
. Do you really need such thing? You can always map
stuff in the viewModel
.