Eximia docs
Lifecycle
How OS-level events flow from the system → SLM Eximia runtime → plugins → JS.
The runtime defines a small set of canonical events. Plugins subscribe to
the ones they need; JS code listens via SLMEximia.on(event, handler).
Canonical events
| Event | Triggered by | Cancellable? | Payload |
|---|---|---|---|
resume | App returns to foreground | no | { wasInBackgroundMs: number } |
pause | App moves to background | no | null |
online | Network becomes reachable | no | `{ type: "wifi" |
offline | Network becomes unreachable | no | null |
keyboardShow | Software keyboard appears | no | { heightPx: number } |
keyboardHide | Software keyboard hides | no | null |
backbutton | Hardware back pressed (Android only) | yes | null |
deeplink | App opened via universal/app link | no | { url: string, source: string } |
notification | Push notification tapped | no | { payload: object, action?: string } |
lowMemory | OS signals low memory | no | null |
Cancellable events: the handler returns true to cancel the default OS
action (e.g. for backbutton, returning true prevents the app from
backgrounding).
Subscription on the JS side
SLMEximia.on("pause", () => {
saveDraft();
});
SLMEximia.on("backbutton", () => {
if (router.canGoBack()) {
router.back();
return true; // cancel default; don't background the app
}
return false;
});
const off = SLMEximia.on("resume", ({ wasInBackgroundMs }) => {
if (wasInBackgroundMs > 60_000) refreshData();
});
// later
off(); // unsubscribe
Multiple subscribers per event are allowed; they're called in
registration order. For cancellable events, any subscriber returning
true cancels the default action.
Subscription on the native side (plugins)
Plugins suscribe by overriding lifecycle hooks on SLMEximiaPlugin:
Android (Kotlin):
class SLMCamera : SLMEximiaPlugin() {
override fun onActivityResult(
requestCode: Int, resultCode: Int, data: Intent?
) {
// resume the pending callback
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray
) { ... }
}
iOS (Swift):
class SLMCamera: SLMEximiaPlugin {
override func onResume() { ... }
override func onDeepLink(url: URL) { ... }
}
The full set of overridable hooks is in
../android/docs/SLMEximiaPlugin.md and
../ios/docs/SLMEximiaPlugin.md.
How an OS event reaches a plugin
1. OS signals event (e.g. Activity.onPause, viewWillDisappear)
2. SLMEximiaActivity / SLMEximiaViewController catches it
3. SLMEximiaLifecycle dispatches to all subscribed plugins, in registration order
4. Each plugin runs its hook
5. SLMEximiaLifecycle then emits an event envelope to JS via the bridge
6. JS subscribers fire
Plugins are called before JS. This is intentional: a plugin (e.g. a session manager) can mutate global state that JS handlers then observe in the same dispatch cycle.
Android: how OS callbacks map
| SLMEximia event | Activity callback |
|---|---|
resume | onResume() (after onStart if app was stopped) |
pause | onPause() |
online / offline | ConnectivityManager.NetworkCallback registered by the runtime |
keyboardShow / keyboardHide | ViewCompat.setOnApplyWindowInsetsListener on the root view, observing IME insets |
backbutton | OnBackPressedDispatcher callback |
deeplink | onNewIntent(intent) when intent has Intent.ACTION_VIEW |
notification | Intent extras when the activity is launched via a PendingIntent from a notification |
lowMemory | onTrimMemory(TRIM_MEMORY_RUNNING_CRITICAL) and friends |
In addition, the runtime forwards onActivityResult and
onRequestPermissionsResult to plugins (these are not JS-visible — they're
delivery mechanisms for in-flight plugin calls).
iOS: how OS callbacks map
| SLMEximia event | iOS source |
|---|---|
resume | applicationDidBecomeActive / sceneDidBecomeActive |
pause | applicationWillResignActive / sceneWillResignActive |
online / offline | NWPathMonitor started by the runtime |
keyboardShow / keyboardHide | UIResponder.keyboardWillShowNotification etc. |
backbutton | not applicable — no event emitted on iOS |
deeplink | scene(_:openURLContexts:) or application(_:continue:restorationHandler:) |
notification | userNotificationCenter(_:didReceive:withCompletionHandler:) |
lowMemory | UIApplicationMain memory warning notification |
Ordering and timing
- All native plugin hooks for a given event run synchronously on the main thread before the JS event envelope is emitted.
- If a plugin hook is
suspend/async, it runs on its dispatcher; the runtime awaits all subscribed hooks before emitting to JS. - JS subscribers run on the WebView's main thread (same as DOM).
A misbehaving plugin that blocks the main thread for >100ms in a
lifecycle hook will trigger an SLMEximiaLifecycleSlow warning in debug
builds and a logged warning in release.
Testing
Each lifecycle event has an instrumentation test that:
- Subscribes a fixture plugin and a JS handler.
- Triggers the OS event (or its closest simulation — e.g. moving
the activity to background via
Lifecycle.State). - Asserts both the plugin hook and the JS handler ran, in the right order, with the right payload.