I have some strange behavior. I have 2 view models...
# compose
l
I have some strange behavior. I have 2 view models which both extend from a base view model. The base view model holds state:
Copy code
var uiState: UiState by mutableStateOf(UiState.Loading)
        private set
I also have 2 screens, each of them uses its own view model. viewModelScreenA and viewModelScreenB. When I print the uiState in both view models like:
Copy code
init {
        Timber.e("VM A - uiState: $uiState")
    }
The uiState of both view models are identical
UiState$Loading@220c954
. I would expect that they are different, not?
e
if it's
Copy code
sealed class UiState {
    object Loading : UiState ()
}
then there's only one
UiState.Loading
instance. why do you expect them to be different?
i
How are you implementing your two screens? Via Navigation Compose or something else?
l
I'm really confused now. I'm using
compose-router
. I have a related problem but I can't figure it out. Forget the second screen. When I enter screen A, I'm doing this:
Copy code
DisposableEffect(Unit) {
        presenter.connect(device)
        onDispose { presenter.disconnect() }
    }

    var connectionState: BluetoothConnectionState by remember {
        mutableStateOf(BluetoothConnectionState.Initial)
    }

    when (val state = presenter.uiState) {
        UiState.Success -> {
            connectionState = BluetoothConnectionState.Connected
        }
        is UiState.Failure -> {
            connectionState = BluetoothConnectionState.Disconnected

            showErrorMessage(state.message)
        }
    }
Within my view model,
uiState
is set to UiState.Success after successfully connecting, but it never steps into Success case in screen. This worked before refactoring my code base.
i
So just to be clear, these are not Jetpack ViewModels, but your own custom class for implementing the view model pattern?
l
Oh sorry, yes, it's custom classes.
ok wow I have found the problem, but it was just luck. I'm not doing this:
Copy code
DisposableEffect(Unit) {
        presenter.connect(device)
        onDispose { presenter.disconnect() }
    }
but rather:
Copy code
DisposableEffect(Unit) {
        onConnect()
        onDispose { presenter.disconnect() }
    }
so I don't have to pass
BluetoothDevice
to make the screen more testable.
onConnect()
looks like:
Copy code
ScreenA(
    presenter = presenterA,
    onConnect = { presenterA.connect(device) },
    ...
I didn't expect that it makes a different, but when changing it to first sample it steps into Success case like expected. Can you explain why the solution with
onConnect
does not work??
Oh fuck, I suck so hard. Sorry I got it. Since I'm doing this when using
onConnect
:
Copy code
ScreenA(
    presenter = presenterA,
    onConnect = { presenterA.connect(device) },
    ...
and this to obtain the presenter:
Copy code
val presenterA: APresenter
        get() = APresenter(someUseCase)
I was dealing with two different presenter objects 🙋‍♀️ This took a while to notice. Sorry guys 🙏
i
Sometimes talking it though is exactly what you need. Glad you got it working
❤️ 1