Skip to content

Core Concepts

Anchor is based on a few core concepts that work together to manage state and side effects in a predictable way.

Overview

The Anchor architecture consists of the following main components:

  • ViewState: The immutable representation of the UI state.
  • Effect: Dependencies required for side effects (e.g., repositories, services).
  • Anchor: The engine that manages state, executes effects, and handles communication.
  • Actions: Functions that define how to modify state or trigger side effects.
  • Signals: One-time messages sent from the Anchor to the UI.
  • Events: Internal messages within the Anchor used for complex logic.

ViewState

ViewState is a marker interface for your UI state. It should typically be an immutable data class.

data class MyState(
    val isLoading: Boolean = false,
    val items: List<String> = emptyList(),
    val error: String? = null
) : ViewState

Effect

Effect is a marker interface for dependencies used in side effects. This allows you to keep your Anchor logic pure and testable by injecting dependencies.

class MyEffect(
    val repository: MyRepository
) : Effect

Anchor

The Anchor is the central component. It manages the ViewState and provides a DSL for:

  • reduce: Updating the state.
  • effect: Executing suspendable side effects.
  • cancellable: Managing long-running jobs that can be cancelled.
  • post: Sending signals to the UI.
  • emit: Emitting internal events.

Anchor<R, S, Err> carries three type parameters: R (Effect), S (ViewState), and Err (domain error type). Use PureAnchor<R, S> when your anchor has no domain errors. See Error Handling for the full error handling guide.

Actions

Actions are usually extension functions on your specific Anchor type. They encapsulate the business logic.

suspend fun MyAnchor.loadData() {
    reduce { copy(isLoading = true) }
    try {
        val result = effect { repository.fetchData() }
        reduce { copy(isLoading = false, items = result) }
    } catch (e: Exception) {
        reduce { copy(isLoading = false, error = e.message) }
    }
}

Signals

Signals are one-time messages for things that aren't part of the persistent state, like showing a Snackbar, a Toast, or navigating.

sealed interface MySignal : Signal {
    data class ShowError(val message: String) : MySignal
}

// In an Action:
post { MySignal.ShowError("Something went wrong") }

// In UI:
HandleSignal<MySignal.ShowError> { signal ->
    snackbarHostState.showSnackbar(signal.message)
}

Events & Subscriptions

Events are internal messages. They are useful for complex logic where one action triggers another, or for reacting to lifecycle events like Created.

subscriptions = {
    listen<Created> { events ->
        events.anchor { loadData() }
    }
}