present
The primary Composable entry point to present a UiState. In production, a Navigator is used to automatically connect this with a corresponding Ui
to render the state returned by this function.
When handling events, embed a eventSink: (Event) -> Unit
property in the state as needed.
data class State(
val favorites: List<Favorite>,
eventSink: (Event) -> Unit
) : CircuitUiState
class FavoritesPresenter(...) : Presenter<State, Event> {
@Composable override fun present(): State {
// ...
return State(...) { event ->
// Handle UI events here
}
}
}
Content copied to clipboard
Dependency Injection
Presenters should use dependency injection, usually assisted injection to accept Navigator or Screen instances as inputs. Their corresponding assisted factories should then be used by hand-written presenter factories.
class FavoritesPresenter @AssistedInject constructor(
@Assisted private val screen: FavoritesScreen,
@Assisted private val navigator: Navigator,
private val favoritesRepository: FavoritesRepository
) : Presenter<State> {
@Composable override fun present(): State {
// ...
}
@AssistedFactory
fun interface Factory {
fun create(screen: FavoritesScreen, navigator: Navigator): FavoritesPresenter
}
}
Content copied to clipboard
Testing
When testing, simply drive UI events with a MutableSharedFlow
use Molecule+Turbine to drive this function.
@Test
fun `emit initial state and refresh`() = runTest {
val favorites = listOf("Moose", "Reeses", "Lola")
val repository = FakeFavoritesRepository(favorites)
val presenter = FavoritesPresenter(repository)
moleculeFlow(Immediate) { presenter.present() }
.test {
assertThat(awaitItem()).isEqualTo(State.Loading)
val successState = awaitItem()
assertThat(successState).isEqualTo(State.Success(favorites))
successState.eventSink(Event.Refresh)
assertThat(awaitItem()).isEqualTo(State.Success(favorites))
}
}
Content copied to clipboard
Note that Circuit's test artifact has a Presenter.test()
helper extension function for the above case.