Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I store the shareable data in sealed class?

Tags:

android

kotlin

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.

like image 548
c-an Avatar asked Sep 15 '25 09:09

c-an


1 Answers

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

like image 81
Chirag Savsani Avatar answered Sep 17 '25 00:09

Chirag Savsani