Adopt jextract JNI callbacks and Compose demo UI - #5
Merged
Conversation
Add AndroidBluetoothBridge target (JExtractSwiftPlugin, JNI mode) exposing one function per callback event, an AndroidCentralRegistry mapping Int64 identifiers to centrals (replacing the unsafe swiftPeer raw pointer), and handle* event methods on AndroidCentral taking primitive values. Peripherals are resolved by address via getRemoteDevice and characteristics matched by UUID and instance id, so callback events no longer need Java object identity.
ScanCallback and BluetoothGattCallback no longer declare external JNI functions or hold a swiftPeer pointer; they extract primitive values from the Android callback objects and forward them to the generated AndroidBluetoothBridge statics, keyed by central identifier and address.
The SwiftAndroidApp dynamic library is now built from BluetoothDemoBridge, a jextract JNI target exposing startScan/stopScan/connect/disconnect with closure parameters that surface as Java functional interfaces. DemoCentral owns the AndroidCentral instance, bootstrapped from the application context via AndroidContext. The Swift-driven View UI, example sources and shell build scripts are removed.
MainActivity renders a Compose scanner screen backed by the generated BluetoothDemoBridge bindings: scan toggle, device list sorted by RSSI, and tap-to-connect with discovered service UUIDs. The SwiftObject/external-fun Kotlin scaffolding and the unused View-based demo classes are removed.
The jextract task cross-compiles the package graph with the Swift Android SDK (--disable-sandbox so the swift-java callbacks sub-build can run Gradle), generated Java bindings and a patched SwiftKitCore source tree are added to the source set, and all required .so files are staged into jniLibs. The Unsigned annotation is replaced with an Android-compatible copy since the upstream source depends on jdk.jfr.
Install a Temurin JDK before the Android build (required by android-commandlinetools and the swift-java jextract plugin) and build with Swift 6.3.3, matching the swift-tools 6.3 requirement of the manifests.
The AndroidBluetoothBridge target now exposes BluetoothScanEventSink and BluetoothGattEventSink as public Swift classes; jextract surfaces each as a Java class with native instance methods, so the Kotlin adapters hold and invoke a Swift instance directly instead of routing events through Int64 identifiers and a global registry. Each sink weakly references its central, preserving the no-dangling-events behavior. Sink identity crosses the JNI boundary once, during adapter construction, via the transient AndroidCentralHandoff window; lifetime is managed by the swift-java arena.
ScanCallback and BluetoothGattCallback now hold a jextract-generated BluetoothScanEventSink/BluetoothGattEventSink and forward events to it; the no-argument constructors obtain the Swift sink for the connection under construction via AndroidBluetoothBridge.takePending*EventSink, and the primary constructors accept any sink instance. Java compatibility moves to 17, which the generated bindings require.
colemancda
force-pushed
the
feature/jextract-jni-callbacks
branch
from
July 25, 2026 22:04
235c656 to
9ef650f
Compare
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.
Summary
Replaces the legacy
@JavaImplementation/swiftPeercallback bridging with swift-java's jextract JNI pattern (following PureSwift/Android), and rewrites the demo app UI in Kotlin Jetpack Compose.Library
AndroidBluetoothBridgetarget processed byJExtractSwiftPlugin(JNI mode): one generated static method per Bluetooth callback event (org.pureswift.bluetooth.bridge.AndroidBluetoothBridge); byte payloads cross asbyte[].AndroidCentralRegistrymaps stableInt64identifiers to weakly-held centrals, replacing the unsafe raw-pointerswiftPeerround-trip.handle*event methods onAndroidCentraltaking primitive values; peripherals resolve by address (getRemoteDevice) and characteristics match by UUID + instance id, removing the need for Java object identity in events.@JavaImplementationis gone from the package (it also crashed the current compiler when applied to nested types).Demo
ScanCallback.kt/BluetoothGattCallback.ktare pure Kotlin data-forwarders — noexternal fun, no pointer field. They remain only because the Android callback types are abstract classes requiring JVM subclasses.BluetoothDemoBridgejextract target exposingstartScan/stopScan/connect/disconnectwith closure callbacks (generated Java functional interfaces).jextracttask,--disable-sandboxfor the swift-java callbacks sub-build), consumes the generated Java + SwiftKitCore as source, and stages all.sos into jniLibs; the shell build scripts are removed. An Android-compatibleUnsignedannotation replaces the upstream one that depends onjdk.jfr.Testing
gradle assembleDebug(JDK 21; callbacks sub-build on JDK 25) builds the APK end-to-end, including jextract generation for both bridge targets.libSwiftAndroidApp.so.@JavaImplementation,external fun, orswiftPeerremaining.