Introduction
Kotlin Multiplatform · ActivityKit + Android 16 Live Updates

Halo

Start, update and end a live activity from commonMain. iOS shows it on the Lock Screen and in the Dynamic Island; Android shows it as a promoted Live Update with a ticking timer.

Halo gives you one typed API in commonMain to start, update and end a system-surfaced status card. iOS renders it with ActivityKit on the Lock Screen and in the Dynamic Island; Android renders it as an Android 16 Live Update (promoted ongoing notification with progress and a chronometer), falling back to an ongoing notification on older versions.

val liveActivities = rememberLiveActivityManager()
 
when (val result = liveActivities.start(LiveActivityRequest(content))) {
    is LiveActivityResult.Success -> sessionId = result.value.id
    is LiveActivityResult.Failure -> show(result.error.code, result.error.message)
}

Screenshots

iOS Lock Screen live activity with a ticking countdowniOS Lock Screen live activity after endAndroid 16 Live Update with progress and countdown chronometerAndroid notification after end

Same LiveActivityContent on both platforms: the default iOS widget on the Lock Screen, and the Android 16 promoted notification with a natively ticking chronometer.

Why Halo

  • Native surfaces, one call site. ActivityKit and Live Updates behind a single interface.
  • The system ticks the clock. LiveActivityTimer is rendered from absolute timestamps; you never send per-second updates.
  • Result-first, typed errors. Every call returns a LiveActivityResult with a stable code.
  • Survives process death. Running activities are restored, content included.
  • Honest about limits. iOS’s eight-hour ceiling surfaces as Expired; payloads over 3 KB are rejected before they reach the platform.