Skip to content

Bridge the evaluation core to the Compose interpreter - #21

Merged
colemancda merged 7 commits into
masterfrom
feature/bridge
Jul 23, 2026
Merged

Bridge the evaluation core to the Compose interpreter#21
colemancda merged 7 commits into
masterfrom
feature/bridge

Conversation

@colemancda

Copy link
Copy Markdown
Member

Fourth step of the Compose-backed architecture (#18 teardown, #19 core, #20 interpreter): the JNI bridge connecting them, wired desktop-first exactly as planned — the five hand-matched signatures were shaken out on the macOS JVM before Android saw them, and they worked on the first live run.

The bridge

  • AndroidSwiftUIBridge (new platform-neutral target, no android.* imports — builds as a macOS dylib and cross-compiles for Android identically):
    • @JavaClass bindings for ViewNode and TreeStore — the safe Swift→Kotlin direction.
    • Materializer: walks the core's RenderNode IR building Kotlin nodes, one JNI call per node with flat arrays; prop values cross as JSON literals (typed model, no per-field crossings, no whole-tree strings).
    • SwiftCallbackSink @JavaImplementation: the entire Kotlin→Swift surface — five methods, documented on both sides as never growing per-view entries.
    • BridgeRuntime: evaluate → materialize → TreeStore.update on every state change. Event callbacks arrive on the platform main thread outside composition, so the synchronous path is safe; a scheduler comes with async state in R5.
  • ViewNode gains the bridge constructor (flat arrays → typed model); the sink lives in a JVM source set both Kotlin targets share.

Desktop rig goes live

SwiftRuntime.load() loads libSwiftJava.dylib first — swift-java initializes in JNI_OnLoad, which the JVM only fires for explicitly loaded libraries — then the app dylib. The rig renders live Swift-evaluated trees, falling back to fixtures when unconfigured.

Android host

SwiftUIHostView (interpreter in a ComposeView) + AndroidSwiftUIApp.run(root:) installing it as the activity content. The umbrella @_exporteds the core, so app code writes plain SwiftUI with one import — AndroidKit must not be imported by app code (its AndroidView.View collides with the core's View); a log helper covers the one thing the demo needed it for.

Verification

  • Desktop (headless, automated): :desktop:jvmTest runs the full round trip — native Swift evaluates the counter, JNI materializes, Compose renders "Count: 0", clicks dispatch through the sink into Swift @State, re-evaluation pushes new trees, UI shows "Count: 1" then "Count: 2". Passing.
  • Emulator: the same counter demo — launches, renders Material 3 button/switch, two taps advance "Count: 0" → "Count: 2" (uiautomator-verified).

Notes

  • The host view doesn't yet inset for system bars (content draws under the status bar) — R5 polish.
  • The exact six exported JNI symbols were verified with nm before any JVM loaded them.

One JNI call per node with flat arrays; prop and modifier-arg values arrive
as JSON literals, keeping the typed model without per-field crossings.
The entire Kotlin-to-Swift surface: five externals in a JVM source set both
targets share, dispatching event ids into the Swift registry.
Platform-neutral target: Kotlin bindings for ViewNode and TreeStore, a
materializer walking the IR into Kotlin nodes, the Swift half of the callback
sink, and a runtime driving evaluate-materialize-update on every state change.
Builds as a macOS dylib and cross-compiles for Android identically.
Loading libSwiftJava first fires swift-java's JNI_OnLoad; the rig then hands
its tree store to the Swift entry point and renders live trees, falling back
to fixtures when no library is configured.
Native Swift evaluates the counter, JNI materializes it, Compose renders it,
clicks dispatch back into Swift state, and the UI shows each new count.
SwiftUIHostView wraps the interpreter in a ComposeView; AndroidSwiftUIApp.run
starts the bridge runtime against its store and installs it as the content
view. The umbrella re-exports the core so app code needs a single import.
@colemancda
colemancda merged commit cd4bd7e into master Jul 23, 2026
6 checks passed
@colemancda
colemancda deleted the feature/bridge branch July 23, 2026 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant