Bridge the evaluation core to the Compose interpreter - #21
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, noandroid.*imports — builds as a macOS dylib and cross-compiles for Android identically):@JavaClassbindings forViewNodeandTreeStore— the safe Swift→Kotlin direction.Materializer: walks the core'sRenderNodeIR 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.updateon 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.ViewNodegains 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()loadslibSwiftJava.dylibfirst — swift-java initializes inJNI_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 aComposeView) +AndroidSwiftUIApp.run(root:)installing it as the activity content. The umbrella@_exporteds the core, so app code writes plain SwiftUI with one import —AndroidKitmust not be imported by app code (itsAndroidView.Viewcollides with the core'sView); a log helper covers the one thing the demo needed it for.Verification
:desktop:jvmTestruns 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.Notes
nmbefore any JVM loaded them.