Guides
One core or several
Most apps need one core. When a second one arrives, a vendor's SDK or a module another team ships, the two load into the same app side by side, each under its own namespace.
One core first#
Put the app's domain in one core and split it into crates inside it. Add a second core only for code that ships on its own.
| One core, several crates | Several cores | |
|---|---|---|
| State and transactions | shared: one store graph, one order of writes | separate: each core has its own |
| Releases | together | each on its own schedule |
| Cost | one runtime | a runtime, its threads and a copy of Undra per core |
The namespace#
A core's namespace names everything it exports, so two cores never collide. By default it is the core's package name in snake case; set it in undra.toml (lowercase letters, digits and _, at most 32 characters).
[core]
path = "core"
namespace = "acme_pay"| What | Named |
|---|---|
| The C symbol, which returns the core's table | acme_pay_undra_api |
| iOS | AcmePayCore.xcframework, one prelinked libacme_pay.a per slice |
| Android | libacme_pay.so |
| Web | acme_pay.wasm |
| The bindings' entry, in Swift, Kotlin and TypeScript | UndraAcmePay |
undra build refuses a namespace that a sibling project already uses, and every runtime refuses to load two cores with one.
The namespace also keeps the default storage apart: each core's Kv, SecureStore, Fs and Db live under undra/<namespace>/ on every platform, so two cores that use the defaults never see each other's data unless you hand them one adapter of your own.
Loading two cores#
Load each core once, through its own entry. Every generated type defaults to the core of its own package, so a call never reaches the wrong one.
Show the code for Swift, Kotlin and TypeScript
try UndraShopCore.load()
try UndraAcmePay.load()
let cart = try Cart() // Shop's core
let quote = try Quote() // AcmePay's coreval shop = UndraShopCore.load()
UndraAcmePay.load()
AndroidPlatformDefaults.install(shop, this) // the ports, per coreawait UndraShopCore.load({ mode: "wasm-main", wasm: new URL(shopWasm, location.href) });
await UndraAcmePay.load({ mode: "wasm-main", wasm: new URL(payWasm, location.href), shared: false });On iOS each slice exports one symbol, so the two libraries link next to each other with no -force_load. On Android each core registers its natives on its bindings' own class. In React Native, loadNative(UndraAcmePay) installs one module per namespace.
Good to know#
- Pass values between cores, not objects. An object belongs to the core that made it, and two cores can issue the same handle numbers.
- Each core closes on its own. Shutting one down leaves the other running; a closed core can load again.
- Two local Swift packages need two directory names. SwiftPM names a local package after its directory.
The two-core test apps load one core under two namespaces on iOS, Android, the JVM and Node, and CI runs them on every change.