I am trying to implement UiState class with sealed class(I am using Jetpack Compose + ViewModel)
The screen should have mutable data in all the state once result is initialized.
So, I created sealed class like this.
sealed class ResultUiState {
private lateinit var result: ResultData
object Prepping : ResultUiState()
object Loading : ResultUiState()
object Idle : ResultUiState()
data class ResultData(val a: String, val b: Int): ResultUiState() {
init {
result = this
}
}
fun getResultData() = result
}
I checked ResultData is created. But if I access like resultUiState.result
it is not initialized.
As far as I know, ResultUiState can't be initiated because it is just like class containers. I think that's the reason why it's not working.
I don't want to create other classes because it doesn't look clean and do all the thing in that sealed class. How can I solve this issue?
I tested in Kotlin Playground and it works as I expected.
import java.lang.ref.SoftReference
sealed class ResultUiState {
lateinit var result: ResultData
object Prepping : ResultUiState()
object Loading : ResultUiState()
object Idle : ResultUiState()
data class ResultData(val a: String, val b: Int): ResultUiState() {
init {
result = this
}
}
fun getResultData() = result
}
fun main() {
var resultUiState : ResultUiState = ResultUiState.Prepping
println("resultUiState: $resultUiState")
resultUiState = ResultUiState.Idle
println("resultUiState: $resultUiState")
resultUiState = ResultUiState.ResultData("Hello", 7)
println("resultUiState: $resultUiState")
println("resultUiState: $resultUiState, result: ${resultUiState.getResultData()}")
resultUiState = ResultUiState.Loading
println("resultUiState: $resultUiState, result: ${resultUiState.getResultData()}")
}
// Result
resultUiState: ResultUiState$Prepping@49c2faae
resultUiState: ResultUiState$Idle@5197848c
resultUiState: ResultData(a=Hello, b=7)
resultUiState: ResultData(a=Hello, b=7), result: ResultData(a=Hello, b=7)
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property result has not been initialized
at ResultUiState.getResult (File.kt:4)
at ResultUiState.getResultData (File.kt:16)
at FileKt.main (File.kt:28)
What I'd like to achieve is, Think about Kiosk. There are menus and price list. The data shouldn't be changed in Loading, Error, etc. But only updates whenever it's Success state.
Here is the example of the Sealed class I use for API calls using a retrofit. You can modify sealed class parameters according to your need.
sealed class Resource<T>(
val data: T? = null,
val message: String? = null,
val errorCode: Int? = 0,
val errorBody: ResponseBody? = null
) {
class Ideal<T>(data: T? = null) : Resource<T>(data)
class Success<T>(data: T) : Resource<T>(data)
class Loading<T>(data: T? = null) : Resource<T>(data)
class Error<T>(data: T? = null, message: String, errorCode: Int?, errorBody: ResponseBody?) :
Resource<T>(data, message, errorCode, errorBody)
}
Observing API Response in Fragment or Activity like this
private fun observeAPIResponse(response: Resource<MY API DATA CLASS>) {
when (response) {
is Resource.Ideal -> {}
is Resource.Loading -> {
}
is Resource.Success -> {
}
is Resource.Error -> {
}
}
In ViewModel class I am setting value to Resource Class like below.
val liveData: MutableLiveData<Resource<MY API DATA CLASS>> = MutableLiveData()
fun getApiResponseList() = viewModelScope.launch(Dispatchers.IO) {
liveData.postValue(Resource.Loading())
try {
if (isNetworkAvailable(app)) {
val apiResult = myusecase.execute()
liveData.postValue(apiResult)
} else {
liveData.postValue(Resource.Error(null, “No Internet Connection”, 1009, null))
}
} catch (e: Exception) {
liveData.postValue(Resource.Error(null,e.message.toString(),null,null))
}
}
You can use postValue method to set value to Resource(Sealed) class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With