MVI Pattern Quiz

ARCHITECTURE › Patterns

What best describes the data flow in MVI?

Answer: State flows to the View, and intents flow up into new state

MVI is unidirectional: immutable state flows down to render the View, while user intents flow up and are folded into a new state by the reducer.

Which signature correctly captures an MVI reducer?

Answer: (state, intent) -> newState, a pure function returning new state

A reducer is a pure function of the current state and an intent/result that returns a new immutable state; it performs no side effects, which makes it deterministic and easy to test.

Why is a single immutable state object central to MVI's debuggability?

Answer: Each transition produces a new snapshot you can log, diff, and replay

Because every transition produces a new immutable snapshot from a deterministic reducer, you can record and replay the exact sequence of states, which is what enables time-travel debugging.

How should a network call's result be incorporated in MVI?

Answer: Do the call as a side effect, then reduce its result into state

Reducers must stay pure, so async work happens outside them; the result re-enters the loop as an intent/result that the reducer reduces into the next immutable state.

When is MVI's extra boilerplate most justified over plain MVVM?

Answer: On complex screens where explicit, replayable transitions cut bugs

The state class, intent hierarchy, reducer, and effect plumbing pay off when state complexity is high; for trivial screens the ceremony outweighs the benefit.

Per Android's architecture recommendations, how should one-off events such as showing a snackbar be handled?

Answer: Handle them in the ViewModel and update the exposed uiState

The guidance is to avoid pushing events from the ViewModel to the UI; instead handle them in the ViewModel and update the single uiState, preserving one source of truth across lifecycle changes.

What is a key structural difference between MVI and MVVM?

Answer: MVI routes input through one intent channel and one immutable state

MVI constrains interaction to a single intent channel and one immutable state object, making transitions explicit; MVVM commonly exposes several observable properties and lets the View call arbitrary methods. MVI is often implemented on top of a ViewModel.

In idiomatic Kotlin, what is the best way to model the set of intents a screen accepts?

Answer: A sealed interface or sealed class, giving compile-time when checks

A sealed interface/class lets intents carry their own payloads and makes a when expression exhaustive at compile time, so adding a new intent forces every handler to address it.

Given an immutable UiState data class, how do you produce the next state that changes just one field?

Answer: Call copy() on the current state and override one field

data class copy() returns a fresh immutable instance with the requested field overridden and all others carried over, which is exactly how a reducer builds the next state without mutation.

In a Compose MVI screen, how is the state typically exposed and consumed?

Answer: Collect UiState with collectAsStateWithLifecycle; pass onIntent down

Collecting the StateFlow with collectAsStateWithLifecycle gives lifecycle-aware state-down rendering, while an onIntent lambda carries events up, realizing the MVI loop natively in Compose.

For a true one-off effect like a single navigation command, why is a Channel (consumed as a Flow) often preferred over a StateFlow?

Answer: A StateFlow replays its latest value, so nav re-fires; Channel fires once

StateFlow is a hot, value-conflating holder that replays its current value to every new collector, which would re-trigger a one-shot navigation on re-collection; a Channel hands each emitted effect to a single consumer once.

Which operation placed inside a reducer would break its purity?

Answer: Reading System.currentTimeMillis() to timestamp the new state

A pure reducer must depend only on its inputs; reading the wall clock introduces nondeterminism, so the timestamp should be captured outside and passed in as part of the intent/result.

An MVI ViewModel reduces a result but the new UiState is structurally equal to the previous one. What keeps the UI from doing redundant work?

Answer: StateFlow uses equals, and Compose skips unchanged inputs

StateFlow conflates and compares values with equals, so an equal state is never re-emitted, and Compose's stability checks then skip recomposing composables whose inputs did not change.

A screen must display data from both a user repository and a settings flow. The MVI-idiomatic approach is to:

Answer: combine the flows and fold them into one immutable UiState StateFlow

MVI keeps a single source of truth, so multiple inputs are combined and folded into one immutable UiState in the ViewModel rather than letting the View reconcile several independent states.

Back to MVI Pattern