Hey, just found Ballast today and I'm going throug...
# ballast
s
Hey, just found Ballast today and I'm going through the docs. In the Follow-up Inputs section, I saw this:
Copy code
when(input) {
    is Inputs.RequestLogout -> {
        loginRepository.logOut()
        postInput(Inputs.ClearCache)
    }
    is Inputs.ClearCache -> {
        updateState { it.copy(user = null) }
    }
And I'm wondering if it's just for the sake of this simple example, or there's a deeper reason why this wouldn't suffice:
Copy code
when(input) {
    is Inputs.RequestLogout -> {
        loginRepository.logOut()
        updateState { it.copy(user = null) }
    }
(btw, I haven't actually tried running any code yet, but this library seems incredibly robust and thought out. Very impressive!!)
c
You’re right, this example is very contrived, it’s mostly just to show the syntax for the case where you have one Input that triggers another. A more real-world example would be something like a screen with a pull-to-refresh action. When you first visit the screen, you send an
Initialize
input (maybe passing along nav args, or something like that). But the user can also pull-to-refresh, which should basically re-do the Initialize input, except you don’t want to pass the nav-args in again so you cannot reuse the same
Initialize
Input. So you might choose to have
Initialize
send the same Input that’s sent when the user pulls-to-refresh, to make sure that the two actions are handled in the same way.
Copy code
object ScreenContract {
    data class State(
        val sortOrder: SortOrder = SortOrder.Ascending,
        val screenItems: List<ScreenItems> = emptyList(),
    )

    sealed class Inputs {
        data class Initialize(val sortOrder: SortOrder) : Inputs()
        object RefreshScreenData : Inputs()
    }
}
Copy code
when(input) {
    is Initialize -> {
        updateState { it.copy(sortOrder = input.navArgs.sortOrder) }
        postInput(Inputs.RefreshScreenData)
    }
    is RefreshScreenData -> {
        val currentState = getCurrentState()
        val screenItems = repository.fetchScreenItems()
        val sortedScreenItems = when(currentState.sortOrder) {
            SortOrder.Ascending -> screenItems.sorted()
            SortOrder.Descending -> screenItems.sortedDescending()
        }
        updateState { it.copy(screenItems = screenItems) }
    }
}
Of course, you could also extract that initialization logic into a separate helper function rather than having one Input send another, but that carries along a lot of boilerplate as you need to declare the receiver of
InputHandlerScope<...>
on that helper function. And when you send follow-up Inputs, it will show up in the Debugger, so makes it really easy to understand what’s going on in the ViewModel without even needing to look at the implementation. You’d see
Initialize
followed immediately after by
RefreshScreenData
s
oh yeah I forgot
handleInput
is an extension function