Skip to content

Lifecycle & states

Beekon goes through exactly five states. They’re identical on every platform — only the casing follows the host language’s idiom (Tracking on Kotlin, tracking on Swift).

Idle → Starting → Tracking ⇄ Paused(reason) → Stopped

You observe state via the state stream — it’s hot and replay-1, so every consumer sees the latest value the moment they subscribe.

StateMeaningPosition emissions?
IdleInitial state. initialize/configure happened, start has not.No
Startingstart() was called; the platform is bringing up its location pipeline.No
TrackingLive updates flowing from the OS provider through the gate.Yes
Paused(reason)Something external interrupted tracking — see pause reasons.No
Stoppedstop() was called; the platform pipeline is torn down. Re-enter via start().No

A shutdown() call is rarely needed — it tears down the state holder entirely. Most apps just leave Beekon at Stopped between sessions.

Pauses happen when a precondition that was true at start() becomes false at runtime. Beekon never throws from a paused state — it transitions and the corresponding error class is what start() would have thrown if you’d called it again from scratch.

ReasonWhen it fires
PermissionRevokedUser revoked location permission from Settings while tracking.
LocationDisabledSystem-wide Location Services were turned off (iOS), or a similar disable on Android.
UnknownCatch-all for transient OS errors. Usually transient — Beekon tries to recover.

Pause reasons match across platforms (permissionRevoked on Swift/Dart, PermissionRevoked on Kotlin).

// state is a StateFlow — replay-1, hot
val state by Beekon.state.collectAsStateWithLifecycle()
when (state) {
is BeekonState.Idle -> Text("Not started")
is BeekonState.Starting -> CircularProgressIndicator()
is BeekonState.Tracking -> Text("Live")
is BeekonState.Paused -> Text("Paused: ${(state as BeekonState.Paused).reason}")
is BeekonState.Stopped -> Text("Stopped")
}
start()
┌────────────────────────┐
│ ▼
Idle ──> Starting ──> Tracking ──> Stopped
▲ │ ▲
│ ▼ │
└─── Paused(reason) ────────┘
permission revoked,
location services off,
transient OS error
  • Starting → Tracking happens after the first OS fix lands (or immediately on iOS 17+).
  • Starting → Paused happens if the precondition fails immediately (e.g. permission was revoked between configure and start).
  • Paused → Tracking happens automatically when the underlying condition recovers (permission re-granted, services re-enabled).
  • Stopped → Tracking requires a fresh start() call.