One core, several hosts
The same core runs under a SwiftUI app, a Jetpack Compose app, a React Native app and a web app at the same time. Each host links what it needs; the logic, the state and the data layer exist once.
What each host links#
| Host | The core | The bindings | The runtime |
|---|---|---|---|
| iOS, SwiftUI | the XCFramework (undra build --platform ios) | generated/swift: @Observable classes | the Swift package |
| Android, Jetpack Compose | jniLibs/<abi>/ (undra build --platform android) | generated/kotlin: StateFlow | the Kotlin modules |
| Web, React | the WebAssembly module, on the main thread or in a Web Worker | generated/ts: hooks (useSignal, useLazyList) | @undra/runtime |
| React Native | the native library, reached over JSI (undra build --platform rn) | generated/ts, the same package as the web | @undra/react-native over @undra/runtime |
One undra.toml lists the platforms; undra build makes every artifact and undra bindgen the three bindings trees. A change to the core changes all four hosts in one commit, and undra schema diff shows what that change means for each of them.
Web and React Native share the TypeScript#
The TypeScript bindings are one package. Under a browser it reaches the WebAssembly core through @undra/runtime; under React Native the same package reaches the native core through @undra/react-native. A hook written for the web app is the hook the React Native app uses; the store code behind it is the same Rust. What differs is only the transport, chosen by the host at start.
A web team's state management goes with it: the mirror is the store. Where a React app selected from a global store, it reads a signal; where it dispatched, it calls a method on the core. See how updates reach the screen and the worker and crash recovery for the web side, and the React Native build for the other.
Moving a React Native app to native#
A team that moves screens from React Native to SwiftUI or Compose keeps the core: the Rust code and its stores do not change with the move. The native screens bind to the Swift or Kotlin classes of the same stores the React Native screens use. One process holds one instance of a core, though: a core image is initialised once per process, so a Swift or Kotlin host in the same app cannot hold the core React Native has loaded (React Native limits). The host therefore switches per app, not per screen: the native app is built against the same core and takes over from the React Native one when its screens are ready. undra adopt adds the core to a native app that already exists without touching its project files.
One core or several#
Several hosts is not the same question as several cores. One core carries an app across every host; a second core is for code that ships on its own, a vendor's SDK or another team's module.