Skip to content

JS → Native

JS consumes · Native provides

This is the primary direction for host / platform capabilities: a native provider registers an implementation, and React Native consumes it through a typed proxy. Both Android (Kotlin) and iOS (Swift) are fully supported.

The JS proxy mirrors the contract’s methods. query becomes an async call; fire returns nothing; querySync reads synchronously.

const connect = useBridge(ConnectHost);
await connect.installEsim({ url, iccId }); // query → Promise<'success' | ...>
connect.showLogin(); // fire → void
const info = connect.getDeviceInfo(); // querySync → value, synchronously

Native implements the generated interface:

class ConnectHostProvider : ConnectHost {
override suspend fun isLoggedIn(): Boolean = session.isLoggedIn()
override suspend fun installEsim(params: InstallEsimParams): InstallEsimResult = /* ... */
override fun showLogin() { /* ... */ }
}
BridgeKit.default.provide(ConnectHostContract) { ConnectHostProvider() }

The provider exposes a platform stream type; BridgeKit collects it once per (binding, stream, params) and multiplexes to every JS subscriber.

override fun otpCodes(): Flow<String> = smsRetriever.codes()
const codes = connect.otpCodes();
const off = codes.subscribe((code) => setOtp(code)); // Unsubscribe
// or: for await (const code of codes) { ... }

Closing the JS subscription cancels the underlying collection. The default backpressure is a lossless bounded buffer (drop-oldest + logged diagnostic on overflow).

Native owns a mutable reactive value; the router subscribes to it and feeds updates into the StateStore. On the JS side a StateMirror subscribes via BridgeState.observe() and serves useBridgeState through useSyncExternalStore.

override val connectivity = MutableStateFlow(Connectivity(online = true))
const { value, status } = useBridgeState(ConnectHost, 'connectivity');
// status: 'initial' | 'available' | 'unprovided'

Two properties make this cheap and total:

  1. get() is always local — it reads the cached mirror, never crossing the bridge.
  2. Hydration on connectconnectDispatcher returns a snapshot of all native-provided state, so JS mirrors are populated the instant the runtime connects. BridgeKitReady implies hydrated mirrors.

Per-hook subscriptions never cross the bridge: there is exactly one multiplexed native observation per (contract, state key), released via unobserve when the JS subscriber count reaches zero.


The mirror image of this page — native calling into JS — is Native → JS (reverse).