iOS
ActivityKit is Swift-only, so the Kotlin manager talks to a small Swift bridge. Three pieces:
1. Swift package (app and widget extension)
Add swift/HaloKMP from the repository as a local package and link it from both
targets. It provides:
HaloActivityAttributes— the singleActivityAttributestype ActivityKit uses to match an activity to its widget. Never copy or re-declare it; a duplicate type means the activity starts but nothing renders.HaloKit— the ActivityKit calls.HaloActivityWidget— a default Lock Screen + Dynamic Island UI.
2. Widget extension
import HaloKMP
import SwiftUI
import WidgetKit
@main
struct MyWidgets: WidgetBundle {
var body: some Widget {
HaloActivityWidget()
}
}Add NSSupportsLiveActivities = YES to the app’s Info.plist. The extension’s
Info.plist needs NSExtensionPointIdentifier = com.apple.widgetkit-extension. See
sample/composeApp/iosApp/project.yml for a complete xcodegen definition.
For a custom look, write your own ActivityConfiguration(for: HaloActivityAttributes.self)
and read context.state (title, subtitle, timerRange, progressFraction, values).
3. Bridge file (app target only)
Copy swift/HaloBridge.swift into the app target, change import ComposeApp to your
Kotlin framework’s name, and register it at launch:
@main
struct MyApp: App {
init() { Halo.shared.register(bridge: HaloBridge()) }
// ...
}Export the library from the framework so Swift sees LiveActivityBridge and LiveActivities:
listOf(iosArm64(), iosSimulatorArm64()).forEach {
it.binaries.framework {
baseName = "ComposeApp"
export(project(":halo"))
}
}
// commonMain: api(project(":halo"))Link the Kotlin framework exactly once; a second copy has its own empty bridge registry.
Behaviour
- Floor is iOS 16.2.
isSupportedis false until the bridge is registered. authorization()isDeniedwhenActivityAuthorizationInfo().areActivitiesEnabledis false.- The system ends activities after eight hours; they surface as
Expired. staleAtEpochMillisbecomesActivityContent.staleDate; the default widget dims stale content.- Payloads over about 4 KB are refused by ActivityKit; the library caps encoded requests at 3 KB.