Kotlin sealed class when expression not working in compose - kotlin

I have following code:
sealed class ChooseCarState {
data class ShowCarsState(val car: Int) : ChooseCarState()
object ShowLoadingState : ChooseCarState()
data class ShowError(val message: String) : ChooseCarState()
}
when observe this sealed class from ViewModel in composable function:
#Composable
fun showCars(){
val state = viewModel.chooseCarState.collectAsState()
when(state.value){
is ChooseCarState.ShowCarsState->{
val car = (state.value as ChooseCarState.ShowCarsState).car
}
is ChooseCarState.ShowLoadingState->{
}
is ChooseCarState.ShowError ->{
val message = (state.value as ChooseCarState.ShowError).message
}
}
}
I got ClassCastException, in spite of the fact that I am casting in is statement.
What could be a problem?

The problem is that from the compiler's point of view, there is no guarantee that state.value will always return the same value, since it is not a final field.
You can solve this by assigning state.value to a variable which you can then safely cast. is then available for smart casts.
#Composable
fun showCars(){
val state = viewModel.chooseCarState.collectAsState()
when(val currentState = state.value){
is ChooseCarState.ShowCarsState->{
val car = currentState.car
}
is ChooseCarState.ShowLoadingState->{
}
is ChooseCarState.ShowError ->{
val message = currentState.message
}
}
}

Related

why my data is coming as null or default in ui from view model in kotlin?

I set up mvvm structure and call two functions in viewmodel. Functions return values, but these values ​​always come as values ​​that I define as null or default.
Why can't I access my data in the view model in the UI?
I can see the data properly in the view model
hear is my code
my ui
if (viewModel.isDialogShown) {
AlertDialog(
onDismiss = {
viewModel.onDismissClick()
},
onConfirm = {
println("First"+viewModel.getFirstConversionRateByCurrency(viewModel.dropDownMenuItem1))
println("SECOND:"+viewModel.getSecondConversionRateByCurrency(viewModel.dropDownMenuItem2))
}
)
}
If the user says confirm in the alert dialog in my UI, these functions are called and I print the returned values ​​for testing purposes, but because I define null as default in the viewmodel, it always comes null.
my view model
#HiltViewModel
class ExchangeMainViewModel #Inject constructor(
private val getConversionRateByCurrencyUseCase: GetConversionRateByCurrencyUseCase
) : ViewModel() {
var second : Double?=null
var first : Double? = null
fun getFirstConversionRateByCurrency(currency:String) : String {
viewModelScope.launch {
first = getConversionRateByCurrencyUseCase.getConversionRateByCurrency(currency)
}
return first.toString()
}
fun getSecondConversionRateByCurrency(currency:String) :String {
viewModelScope.launch {
second = getConversionRateByCurrencyUseCase.getConversionRateByCurrency(currency)
}
return second.toString()
}
}
Also, I defined the viewmodel in ui as you can see the below like this, could it be because of this?
#Composable
fun DropDownMenu(
viewModel: ExchangeMainViewModel = hiltViewModel()
) {
The result in the console is as follows
All those functions your are calling are executing a coroutine from viewModelScope.launch{…} and at the time you launched them you immediately return a value from the method return.xxx.toString() where the xxx (first and second) doesn't have a value yet since what you are expecting is coming from a concurrent execution not a sequential one.
viewModelScope.launch { // launch me separately and wait for any value that will set the `first`
first = getConversionRateByCurrencyUseCase.getConversionRateByCurrency(currency)
}
// return from this function now with a null value, since it was initialized as null
return first.toString()
This is a rough implementation, though I'm not sure if this would work, but this should give you a headstart
Your new ViewModel
#HiltViewModel
class ExchangeMainViewModel #Inject constructor(
private val getConversionRateByCurrencyUseCase: GetConversionRateByCurrencyUseCase
) : ViewModel() {
var conversionValue by mutableStateOf<ConversionValues?>(null)
fun getFirstConversionRateByCurrency(currency:String) {
viewModelScope.launch {
val first = getConversionRateByCurrencyUseCase.getConversionRateByCurrency(currency)
val second = getConversionRateByCurrencyUseCase.getConversionRateByCurrency(currency)
conversionValue = ConversionValues(first, second)
}
}
}
the Data Class for first and second
data class ConversionValues(
val first : Double,
val second: Double
)
and your composable
#Composable
fun DropDownMenu(
viewModel: ExchangeMainViewModel = hiltViewModel()
) {
val conversionValue = vieModel.conversionValue
if (viewModel.isDialogShown) {
AlertDialog(
onDismiss = {
viewModel.onDismissClick()
},
onConfirm {
println("First" + conversionValue.first)
println("SECOND:"+conversionValue.second)
}
)
}
}

How can I know which the subclass of sealed class will return when I use Compose in Android Studio?

The Result<out R> is a sealed class which hold three subclass Success, Error and Loading.
The fun Greeting is #Composable.
By my design, I define queryList as Result class, and it is assigned as Loading first, then it will be Success or Error.
1: But the following code can't be compiled as the following error information, what's wrong with my Code?
2: Is there a better solution for my design?
Compile error
Property delegate must have a 'getValue(Nothing?, KProperty>)' method. None of the following functions are suitable.*
#Composable
fun Greeting(
name: String,
mViewMode:SoundViewModel= viewModel()
) {
Column() {
//The following code cause error.
val queryList by produceState(initialValue = Result<Flow<List<MRecord>>>.Loading ) {
value = mViewMode.listRecord()
}
when (queryList){
is Loading -> { ...}
is Error -> { ...}
is Success -> {...}
}
}
}
class SoundViewModel #Inject constructor(): ViewModel()
{
fun listRecord(): Result<Flow<List<MRecord>>>{
return aSoundMeter.listRecord()
}
}
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
Since queryList is backed by a delegate, it can not be final.
This means in theory, each time you access it, it might hold a different value. The kotlin compiler is very pessimistic about this and assumes that between the time the is Result.Success branch of your when statement is selected and val mydata = queryList.data is executed, the value of queryList might have changed.
To solve this, you can assign the current value of queryList to a final variable and work with that one instead:
when (val currentList = queryList) {
is Result.Error -> {}
is Result.Loading -> {}
is Result.Success -> {
SomeComposable(currentList.data) //currentList is properly smart-cast to Result.Success
}
}

Implementing observable properties that can also serialize in Kotlin

I'm trying to build a class where certain values are Observable but also Serializable.
This obviously works and the serialization works, but it's very boilerplate-heavy having to add a setter for every single field and manually having to call change(...) inside each setter:
interface Observable {
fun change(message: String) {
println("changing $message")
}
}
#Serializable
class BlahVO : Observable {
var value2: String = ""
set(value) {
field = value
change("value2")
}
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
println(BlahVO().apply { value2 = "test2" })
correctly outputs
changing value2
{"value2":"test2"}
I've tried introducing Delegates:
interface Observable {
fun change(message: String) {
println("changing $message")
}
#Suppress("ClassName")
class default<T>(defaultValue: T) {
private var value: T = defaultValue
operator fun getValue(observable: Observable, property: KProperty<*>): T {
return value
}
operator fun setValue(observable: Observable, property: KProperty<*>, value: T) {
this.value = value
observable.change(property.name)
}
}
}
#Serializable
class BlahVO : Observable {
var value1: String by Observable.default("value1")
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
println(BlahVO().apply { value1 = "test1" }) correctly triggers change detection, but it doesn't serialize:
changing value1
{}
If I go from Observable to ReadWriteProperty,
interface Observable {
fun change(message: String) {
println("changing $message")
}
fun <T> look(defaultValue: T): ReadWriteProperty<Observable, T> {
return OP(defaultValue, this)
}
class OP<T>(defaultValue: T, val observable: Observable) : ObservableProperty<T>(defaultValue) {
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
super.setValue(thisRef, property, value)
observable.change("blah!")
}
}
}
#Serializable
class BlahVO : Observable {
var value3: String by this.look("value3")
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
the result is the same:
changing blah!
{}
Similarly for Delegates.vetoable
var value4: String by Delegates.vetoable("value4", {
property: KProperty<*>, oldstring: String, newString: String ->
this.change(property.name)
true
})
outputs:
changing value4
{}
Delegates just doesn't seem to work with Kotlin Serialization
What other options are there to observe a property's changes without breaking its serialization that will also work on other platforms (KotlinJS, KotlinJVM, Android, ...)?
Serialization and Deserialization of Kotlin Delegates is not supported by kotlinx.serialization as of now.
There is an open issue #1578 on GitHub regarding this feature.
According to the issue you can create an intermediate data-transfer object, which gets serialized instead of the original object. Also you could write a custom serializer to support the serialization of Kotlin Delegates, which seems to be even more boilerplate, then writing custom getters and setters, as proposed in the question.
Data Transfer Object
By mapping your original object to a simple data transfer object without delegates, you can utilize the default serialization mechanisms.
This also has the nice side effect to cleanse your data model classes from framework specific annotations, such as #Serializable.
class DataModel {
var observedProperty: String by Delegates.observable("initial") { property, before, after ->
println("""Hey, I changed "${property.name}" from "$before" to "$after"!""")
}
fun toJson(): String {
return Json.encodeToString(serializer(), this.toDto())
}
}
fun DataModel.toDto() = DataTransferObject(observedProperty)
#Serializable
class DataTransferObject(val observedProperty: String)
fun main() {
val data = DataModel()
println(data.toJson())
data.observedProperty = "changed"
println(data.toJson())
}
This yields the following result:
{"observedProperty":"initial"}
Hey, I changed "observedProperty" from "initial" to "changed"!
{"observedProperty":"changed"}
Custom data type
If changing the data type is an option, you could write a wrapping class which gets (de)serialized transparently. Something along the lines of the following might work.
#Serializable
class ClassWithMonitoredString(val monitoredProperty: MonitoredString) {
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
fun main() {
val monitoredString = obs("obsDefault") { before, after ->
println("""I changed from "$before" to "$after"!""")
}
val data = ClassWithMonitoredString(monitoredString)
println(data.toJson())
data.monitoredProperty.value = "obsChanged"
println(data.toJson())
}
Which yields the following result:
{"monitoredProperty":"obsDefault"}
I changed from "obsDefault" to "obsChanged"!
{"monitoredProperty":"obsChanged"}
You however lose information about which property changed, as you don't have easy access to the field name. Also you have to change your data structures, as mentioned above and might not be desirable or even possible. In addition, this work only for Strings for now, even though one might make it more generic though.
Also, this requires a lot of boilerplate to start with. On the call site however, you just have to wrap the actual value in an call to obs.
I used the following boilerplate to get it to work.
typealias OnChange = (before: String, after: String) -> Unit
#Serializable(with = MonitoredStringSerializer::class)
class MonitoredString(initialValue: String, var onChange: OnChange?) {
var value: String = initialValue
set(value) {
onChange?.invoke(field, value)
field = value
}
}
fun obs(value: String, onChange: OnChange? = null) = MonitoredString(value, onChange)
object MonitoredStringSerializer : KSerializer<MonitoredString> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MonitoredString", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: MonitoredString) {
encoder.encodeString(value.value)
}
override fun deserialize(decoder: Decoder): MonitoredString {
return MonitoredString(decoder.decodeString(), null)
}
}

Pattern to avoid if else chain methods call using kotlin sealed class and enums

I've a question about, how would you handle this case?
Imagine that you have to do a validation of an object and that validation should have a sort of importance, in this case we only have 3 validations, each one can result Valid or his own QualityCheck enum value.
This is the method example in kotlin and the validations
sealed class Validation {
abstract fun validate(bobject: ObjectToCheck): QualityCheck
object VeryImportantValidation : Validation() {
override fun validate(bobject: ObjectToCheck): QualityCheck =
if (isValid(bobject.valueX)) QualityCheck.Valid
else QualityCheck.VeryImportantInvalid
}
object SecondMostImportant : Validation() {
override fun validate(bobject: ObjectToCheck): QualityCheck =
if (isValid(bobject.valueNotSoImportant)) QualityCheck.Valid
else QualityCheck.SecondMostImportantInvalid
}
object NotSoImportant : Validation() {
override fun validate(bobject: ObjectToCheck): QualityCheck =
if (isValid(bobject.valueNothingImportant)) QualityCheck.Valid
else QualityCheck.NotSoImportantInvalid
}
}
fun getQualityCheck(object: ObjectToCheck): QualityCheck =
if (VeryImportantValidation.validate(object) === QualityCheck.Valid) {
if (SecondMostImportant.validate(object) === QualityCheck.Valid) {
NotSoImportant(paymentsRepository.getSystemPayments()).validate(object)
} else {
QualityCheck.SecondMostImportantInvalid
}
} else {
QualityCheck.VeryImportantInvalid
}
I think this is not scalable neither easy to read/understand or modify if we would want to add a new one.
There is any kind to do this elegant and easier to include more validations?
If you invert your Boolean conditions, you can eliminate the nesting. Then you can change it to a when statement for simplicity:
fun getQualityCheck(object: ObjectToCheck): QualityCheck = when {
VeryImportantValidation.validate(object) !== QualityCheck.Valid ->
QualityCheck.VeryImportantInvalid
SecondMostImportant.validate(object) !== QualityCheck.Valid ->
QualityCheck.SecondMostImportantInvalid
else ->
NotSoImportant(paymentsRepository.getSystemPayments()).validate(object)
}
Validation like this is a perfect candidate for the "Rules engine pattern"... mostly known as a for loop.
You just set up a List<Validation> with all of the validations you want to run and iterate over them calling the validate method. You have 2 options, collect all errors (doing a fold on the list), or stop the loop after the first error with a asSequence().map().takeWhile().
I forgot to say, you don't need to seal the Validation class. What is your intent with that?
Scalability/Extensibility would depend from situation to situation and a code cannot be open to all types of changes. One rule of thumb is to keep it as simple as possible and when a requirement is changed we ensure that the code is open to such kind of changes.
Also, I agree with #Augusto. Your use of the sealed class is not really how it is intended to be used.
Anyways let's look at how it would be easier to add a new validation, change the severity of the violation, or have several validations with the same severity.
Lets define an interface for Validations.
interface Validation {
fun validate(value: Int): Boolean
}
Now let's define a few Validations
class LimitValidation: Validation{
override fun validate(value: Int) = value < 100
}
class PositiveValidation: Validation {
override fun validate(value: Int) = value > 0
}
class EvenValidation: Validation {
override fun validate(value: Int) = value % 2 == 0
}
Let's say you have the following Violations
enum class Violation {
SEVERE,
MODERATE,
TYPICAL
}
We can make use of sealed class to define the quality.
sealed class Quality {
object High : Quality()
data class Low(val violation: Violation) : Quality()
}
We can create a class responsible for checking the Quality.
class QualityEvaluator {
private val violationMap: MutableMap<KClass<*>, Violation> = mutableMapOf()
init {
violationMap[LimitValidation::class] = Violation.SEVERE
violationMap[PositiveValidation::class] = Violation.MODERATE
violationMap[EvenValidation::class] = Violation.TYPICAL
}
fun evaluateQuality(value: Int, validations: List<Validation>) : Quality {
val sortedValidations = validations.sortedBy(::violationFor)
sortedValidations.forEach {
if(!it.validate(value)) {
return Quality.Low(violationFor(it))
}
}
return Quality.High
}
private fun <T: Validation> violationFor(validation: T): Violation {
return if (violationMap.containsKey(validation::class)) {
requireNotNull(violationMap[validation::class])
} else {
Violation.TYPICAL
}
}
}
Finally, we can use all this like so:
val validations = listOf(LimitValidation(), PositiveValidation(), EvenValidation())
when(val quality = QualityEvaluator().evaluateQuality(8, validations)) {
is Quality.High -> println("Quality is High")
is Quality.Low -> println("Quality is Low. Violation: ${quality.violation}")
}

Use a class from a list of generic interface

I am trying to implement a QueryBus. Basically, I want to register a list of QueryHandlers. Each QueryHandler implements a handle method defined by an interface. Each QueryHandler is associated to a Query. I want to be able to retrieve a QueryHandler using the Query and call handle on it.
The thing is the handle has to be generic because each QueryHandler handles a Query differently. They all take a dedicated Query and may return whatever they want.
interface Query<R>
interface QueryHandler<R, Q : Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
class ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
class GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = mapOf(
with(ListBooksQueryHandler()) {this.listenTo() to this},
with(GetBookQueryHandler()) {this.listenTo() to this}
)
val command = ListBooksQuery()
val result = queryHandlers[command::class.toString()].handle(command)
// Should print the list of BookDto
print(result)
}
I don't even know if its possible, to be honest.
UPDATE 1:
I changed the usage example in the main to show what I am really trying to do. The List was for (bad?) demonstration purpose. I want to store the QueryHandlers and retrieve them from a map.
Additional resources:
Here is what I really want to do:
https://gist.github.com/ValentinTrinque/76b7a32221884a46e657090b9ee60193
UPDATE I've read your gist and tried to come up with a solution that will provide a clean interface to the user of the QueryBusMiddleware.
Note that I used objects instead of classes for the QueryHandler implementations, which felt more natural to me (since there is only one possible entry in the map for each Query implementation).
interface Query<R>
interface QueryHandler<R, Q: Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
object ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
object GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = listOf(
ListBooksQueryHandler,
GetBookQueryHandler
)
val dispatcher: QueryBusMiddleware = QueryDispatcherMiddleware(queryHandlers)
// Calling query bus
val query = ListBooksQuery()
// Result should be List<BookDto>
val result = dispatcher.dispatch(query)
print(result)
}
interface QueryBusMiddleware {
fun <R, Q : Query<R>> dispatch(query: Q): R
}
class QueryDispatcherMiddleware constructor(handlers: List<QueryHandler<*, *>>) : QueryBusMiddleware {
private val handlers = HashMap<String, QueryHandler<*, *>>()
init {
handlers.forEach { handler -> this.handlers[handler.listenTo()] = handler }
}
override fun <R, Q : Query<R>> dispatch(query: Q): R {
val queryClass = query::class.toString()
val handler = handlers[queryClass] ?: throw Exception("No handler listen to the query: $queryClass")
return handler::class.members.find { it.name == "handle" }!!.call(handler, query) as R
}
}