Repository navigation
Samples: Swift 6 JNI sample used from Kotlin, with main-actor view models (on swiftlang#938) - #11
Open
piercifani wants to merge 2 commits into
Open
piercifani wants to merge 2 commits into
piercifani wants to merge 2 commits into
Conversation
This was referenced Nov 3, 2026
A jextract (JNI mode) sample whose Swift modules build in Swift 6 language mode and whose bindings are exercised from Kotlin tests: one test file per feature under src/test/kotlin. KotlinSampleModels is a separate module, so types (and later protocols) cross module boundaries. The baseline covers what works today: a struct, a throwing function, an async function awaited as a coroutine, an enum with a payload, and a type from another module. Added to the CI sample matrix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kotlin test for synchronous @mainactor bindings. It runs on macOS only, where the JVM test thread can be the process's main thread (-XstartOnFirstThread), as Android's UI thread is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
piercifani
force-pushed
the
jni/01-main-actor-thunks
branch
from
November 3, 2026 15:56
8bce9a6 to
52c0842
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.
Stack 1/21 · base:
upstream/pr-938· next: #2 (jni/02-closures-jextracted-types)This PR's base,
upstream/pr-938, is the exact head commit (f6c879d5) of upstream swiftlang/swift-java#938 by Himeshi (teamhimeh on GitHub), unchanged and Apache-2.0. The rest of this stack builds on it. Like any upstream code it stays out of this fork's PR diffs, so this PR reviews only the Kotlin sample commits. When swiftlang#938 merges upstream,maingets synced, this PR is retargeted tomain, andupstream/pr-938is deleted.This replaces #1, which carried the swiftlang#938 commit inside the PR. GitHub doesn't allow changing the base of the first PR in a stack, so it was reopened here.
What swiftlang#938 does (in the base branch)
JNI extraction kept synchronous Java signatures for
@MainActorAPIs but emitted nonisolated Swift thunks, so the generated bindings didn't compile when they touched isolated initializers, methods, properties or subscripts.@MainActorwhen the declaration, its enclosing type or its extension is explicitly main-actor isolated.MainActor.preconditionIsolated()before converting arguments. Java callers must already be on the main actor's executor; there's no hop.nonisolatedmembers and async downcalls are unchanged.Why Skip needs it
In Skip Fuse apps every view model is a
@MainActorclass read from Compose on the UI thread. In a production Skip Fuse app (16 Skip modules), 4,180 bridged entrypoints are main-actor isolated, and all 190 view models write@MainActorexplicitly, so swiftlang#938's explicit-attribute detection covers them.Kotlin in this PR
This PR's first commit adds
Samples/SwiftJavaExtractJNIKotlinSampleApp. Its Swift modules build in Swift 6 language mode, and its bindings are used from Kotlin: one test file per feature,kotlin.testpluskotlinx-coroutinesforawait()on the generated futures. It's in the CI sample matrices like the other samples. Each later PR in the stack adds its own Swift fixture and Kotlin test, so every PR shows the Kotlin it enables.BaselineTest.kt: what works onmaintoday. A struct, a throwing function, anasyncfunction awaited as a coroutine, an enum with a payload, and a type from the other module.MainActorTest.ktwithMainActorViewModel.swift: a@MainActorview model called synchronously from Kotlin. It runs on macOS only, where the JVM test thread can be the process's main thread (-XstartOnFirstThread), as Android's UI thread is; on Linux the JVM never runs tests on the process's main thread.Extra validation done here
./gradlew :Samples:SwiftJavaExtractJNIKotlinSampleApp:testpasses (the baseline tests also pass onmainwithout this PR).@MainActorview model, called from Kotlin on the JVM, passes when the test JVM runs on the process's first thread (-XstartOnFirstThreadon macOS, which matches Android's UI thread being the process main thread). Off the main thread the JVM stops with SIGTRAP from the precondition, as intended.Note for Skip: today Skip's bridge uses
assumeMainActorUnchecked, which lets off-main calls through silently. With this change they trap, which will surface latent threading bugs in apps.🤖 Generated with Claude Code