Hi, the state objects i define end up containing a...
# orbit-mvi
r
Hi, the state objects i define end up containing a lot of nulls. The examples I’ve seen usually use numerics or lists which are easy to initialise to non-null values. But what about strings or complex data structures? I attempted to make my state object a sealed class hierarchy but I couldnt work out how to reduce it. I guess I am looking for validation that I’m doing it correctly (as I can’t find any complex examples to learn from)
m
There are no hard and fast rules. Sometimes it will be easier to go with nullables, sometimes a sealed class is better. The downside of using a sealed class is that often when you reduce you need to break down the possibilities using
when
. It can get complicated. It might be worth extracting the reducers if that’s the case. I think someone over here used https://github.com/Tinder/StateMachine with such an approach to make it more manageable. This increases complexity and indirection so it has its downsides too. TL;DR no perfect solution for this exists :)
🙌 1
🆙 1
a
cc @Guilherme Delgado
g
Hello! For the state itself I’ve used a
data class
to control the state changes (reduce) I’ve used a FSM (that one from Tinder) to help “connecting”. Small example: ViewModel:
Copy code
//entry point called from View
fun start() {    stateMachine.transition(TimerState.Event.OnStart)    }
Copy code
//state machine
state<TimerState.State.Idle> {
            ...
            on<TimerState.Event.OnStart> {
                transitionTo(TimerState.State.CountingDown, TimerState.SideEffect.StartCountDown)
            }
        }
...
onTransition {
            ...
            when (val effect = validTransition.sideEffect) {
                ...
                is TimerState.SideEffect.StartCountDown -> play()
            }
        }
Copy code
//state change
private fun play() {
        intent { ...
                 reduce { ...
                       state.play() // state is a data class and play() it's a helper method to change some val values, ex:  fun play() = copy(isCountingDown = true, ...)
                    }
                }
            }
        }
    }
Hope it helps
it adds more code (the FSM part) but for me it makes the code very clear and it describes itself easily.
I intend to open source this project, but I’m on hold ’cause it’s KMM 😅
🙏 1
m
In our case working with @Anang Kur workaround using this approach:
Copy code
// in viewmodel
sealed class States {
    object Initial : States()
    object UserFetched : States()
}

data class VerificationState(
    val agent: Agent? = null,
    val states: States = States.Initial,
)

// for example, we use Nothing for side-effect

class OurViewModel(
    // dependencies
) : ViewModel(), ContainerHost<VerificationState, Nothing> {

    override val container: Container<VerificationState, Nothing> = container(initialState = VerificationState())

    fun getUser(token: String) = intent {
        runCatching {
            agentUseCase(token)
        }.onSuccess {
            it.collect { agent ->
                reduce {
                    state.copy(agent = agent, states = States.UserFetched) // switch the state(s) here
                }
            }
        }.onFailure {
            postSideEffect(...) // do something when failures
        }
    }
the
Tinder/StateMachines
seems a good alternative in our parts, thanks @Guilherme Delgado. Might need to checks on it for our usecase and interopability later!
Copy code
// our Activity
    // onCreate
    viewModel.observe(this, state = ::render)

    private fun render(state: VerificationState) {
        when (state.states) {
            States.Initial -> {}
            States.UserFetched -> successGetUser(state.agent)
        }
    }
Anyone have tried of https://github.com/nsk90/kstatemachine as alternative for state machine? Recently found the KStateMachine from this article: https://medium.com/@karthik.dusk/state-machine-in-android-kotlin-4f04e4121062 and this discussion about Tinder/StateMachine comparisons: https://github.com/nsk90/kstatemachine/discussions/29#discussioncomment-2447595
g
Thanks for sharing, gonna take a look when possible 😉
❤️ 1