# \[GSoC 2025\] New JNI mode added to swift-java jextract tool!

**URL:** <https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858>\
**Category:** Java interoperability\
**Tags:** android, java, interoperability, gsoc-2025\
**Created:** [August 28, 2025, 4:36pm UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858 "2025-08-28T16:36:11Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![madsodgaard](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/madsodgaard/32/43318_2.png) [@madsodgaard](https://forums.swift.org/u/madsodgaard)\
**Post date:** [August 28, 2025, 4:36pm UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/1 "2025-08-28T16:36:11Z")

</div>

Hi!

My name is Mads and I am excited to share with you what I have been working on for Swift/Java interoperability over the summer with my mentor @ktoso for Google Summer of Code 2025.

# Overview

The [swift-java](https://github.com/swiftlang/swift-java) interoperability library provides the `swift-java jextract` tool, which automatically generates Java sources that are used to call Swift code from Java. Previously, this tool only worked using the [Foreign Function and Memory API (FFM)](https://docs.oracle.com/en/java/javase/21/core/foreign-function-and-memory-api.html), which requires JDK 22+, making it unavailable on platforms such as Android. The goal of this project was to extend the jextract tool, such that it is able to generate Java sources using JNI instead of FFM and thereby allowing more platforms to utilize Swift/Java interoperability.

I am very glad to report that we have succeeded in that goal, supporting even more features than initially planned! Our initial goal was to achieve feature parity with the FFM mode, but the new JNI mode also supports additional Swift language features such as enums and protocols!

With the outcome of this project, you can now run the following command to automatically generate Java wrappers for your Swift library using JNI, therefore opening up the possibility of using it on platforms such as Android.

```swift
swift-java jextract --swift-module MySwiftLibrary \
  --mode jni \
  --input-swift Sources/MySwiftLibrary \
  --output-java out/java \
  --output-swift out/swift   

```

# How does it work?

The FFM mode already had the logic needed to analyze `.swift` or `.swiftinterface` files to gather the needed types, functions, variables, etc. for extraction. A lot of this logic was tied into the FFM based extraction, and therefore the initial step was to separate the analysis phase and the code-generation phase. This was done by introducing a protocol `Swift2JavaGenerator`, which has two implementations: one for FFM and one for JNI. In the future you could add more, such as Kotlin.

Having separated these two phases, we could start working on generating code that uses JNI. Fortunately, the `swift-java` project has since the beginning included JNI support using the `JavaKit` library _(recently renamed to `SwiftJava`)_. This means that developers could write Swift code that uses JNI in a safe and ergonomic way. Instead of generating raw JNI code, the source-generated code depends on `SwiftJava` and uses the conversion functions such as `getJNIValue()`, `init(fromJNI:)` for basic primitive types such as `Int64`, `Bool` and `String`

Each Swift class/struct is extracted as a single Java `class`. Functions and variables are generated as Java methods, that internally calls down to a native method that is implemented in Swift using `@_cdecl`. Take a look at the following example:

```swift
public class MySwiftClass {
  public let x: Int64
  public init(x: Int64) {
    self.x = x
  }

  public func printMe() {
    print(“\(self.x)”);
  }
}

```

It is roughly generated to the equivalent Java `class`:

```java
public final class MySwiftClass implements JNISwiftInstance {
  public static MySwiftClass init(long x, long y, SwiftArena swiftArena$) {
    return MySwiftClass.wrapMemoryAddressUnsafe(MySwiftClass.$init(x, y), swiftArena$);
  }
  
  public long getX() {
    return MySwiftClass.$getX(this.$memoryAddress());
  }

  public void printMe() {
    MySwiftClass.$printMe(this.$memoryAddress());
  }

  private static native long $init(long x, long y);
  private static native long $getX(long self);
  private static native void $printMe(long self);
}

```

We also generate additional Swift thunks that actually implement the `native` methods and call the underlying Swift methods.

> You might notice that we are calling functions such as `$memoryAddress()` and `wrapMemoryAddressUnsafe`(). And why are we passing down a `long` to the native functions? Basically, the JNI wrappers store the address of the corresponding Swift instance and use it to pass it back to Swift in the native calls, allowing Swift to reconstruct a pointer to the instance and calling the respective function such as `printMe()`.

You can learn more about how the memory allocation and management works in the memory section!

# What works?

As mentioned, we initially set the goal of achieving feature parity with the FFM mode. However, we have gone a bit beyond that and achieved support for a few more convenient language features. This includes support for enums _(with associated values)_ and protocols!

Below is a table that describes the supported features in JNI compared to FFM at the time of writing this. For a full and up-to-date list, please see the [Supported Features](https://swiftpackageindex.com/swiftlang/swift-java/main/documentation/swiftjavadocumentation/supportedfeatures#JExtract-calling-Swift-from-Java) page.

| Swift Feature | FFM | JNI |
| --- | --- | --- |
| Initializers: `class`, `struct` | ✅ | ✅ |
| Optional Initializers / Throwing Initializers | ❌ | ✅ |
| Deinitializers: `class`, `struct` | ✅ | ✅ |
| `enum` | ❌ | ✅ |
| Global Swift `func` | ✅ | ✅ |
| Class/struct member `func` | ✅ | ✅ |
| Throwing functions: `func x() throws` | ❌ | ✅ |
| Stored properties: `var`, `let` (with `willSet`, `didSet`) | ✅ | ✅ |
| Computed properties: `var` (incl. `throws`) | ✅ | ✅ |
| Generic parameters in functions: `func f<T: A & B>(x: T)` | ❌ | ✅ |
| Protocols: `protocol` | ❌ | ✅ |
| Existential parameters `f(x: any SomeProtocol) ` | ❌ | ✅ |
| Existential parameters `f(x: any (A & B)) ` | ❌ | ✅ |
| Foundation Data and DataProtocol: `f(x: any DataProtocol) -> Data` | ✅ | ❌ |
| Opaque parameters: `func take(worker: some Builder) -> some Builder` | ❌ | ✅ |
| Optional parameters: `func f(i: Int?, class: MyClass?)` | ✅ | ✅ |
| Optional return types: `func f() -> Int?`, `func g() -> MyClass?` | ❌ | ✅ |
| Primitive types: `Bool`, `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `Float`, `Double` | ✅ | ✅ |
| Parameters: JavaKit wrapped types `JavaLong`, `JavaInteger` | ❌ | ✅ |
| Unsigned primitive types: `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` | ✅ | ✅ |
| String (with copying data) | ✅ | ✅ |
| Pointers: `UnsafeRawPointer`, UnsafeBufferPointer (?) | 🟡 | ❌ |
| Non-escaping `Void` closures: `func callMe(maybe: () -> ())` | ✅ | ✅ |
| Non-escaping closures with primitive arguments/results: `func callMe(maybe: (Int) -> (Double))` | ✅ | ✅ |
| Swift type extensions: `extension String { func uppercased() }` | 🟡 | 🟡 |
| Automatic Reference Counting of class types / lifetime safety | ✅ | ✅ |

# Memory

An interesting aspect of an interoperability library such as `swift-java` is the memory management between the two sides, in this case the JVM and Swift. The FFM mode uses the FFM APIs around `MemorySegment` to allocate and manage native memory. We are not so lucky in JNI. In older Java versions there are different ways of allocating memory, such as `Unsafe` or `ByteBuffer.allocateDirect()`. We could have decided to use these and allocate memory on the Java side, like FFM, but instead we decided to move the responsibility to Swift, which allocates the memory instead. This had some nice upsides, as we did not have to mess the the witness tables like FFM does.

> For more info on memory in FFM, I strongly recommend watching Konrad’s talk [try! Swift Tokyo 2025 - Foreign Function and Memory APIs and Swift/Java interoperability](https://www.youtube.com/watch?v=vgtzhTOhEbs)

The most obvious place we need to allocate memory is when we initialize a wrapped Swift `class`. Take a look at the following generated code for a Swift initializer:

```java
public static MySwiftClass init(SwiftArena swiftArena$) {
  return MySwiftClass.wrapMemoryAddressUnsafe(MySwiftClass.$init(), swiftArena$);
}
private static native long $init();

```

Here we see that we are calling a native method `$init` which returns a `long`. This value is a pointer to the Swift instance in the memory space of Swift. It is passed to `wrapMemoryAddressUnsafe`, which is basically just storing the pointer in a local field and registering the wrapper to the `SwiftArena`.

`SwiftArena` is a type that is used to ensure we eventually deallocate the memory when the Java wrapper is no longer needed. There exists two implements of this:

1. `SwiftArena.ofConfined()`: returns a confined arena which is used with _try-with-resource_, to deallocate all instances at the end of some scope.
2. `SwiftArena.ofAuto()`: returns an arena that deallocates instances once the garbage-collector has decided to do so.

This concept also exists in the FFM mode, and I recommend watching Konrad’s talk to learn more about them!

> Along with the JNI mode we also added support for providing `--memory-management-mode allow-global-automatic` to the tool, to allow a global `ofAuto()` arena as a default parameter, removing the need for explicitly passing around `SwiftArena`s.

If we take a look at the native implementation of `$init` in Swift, we see how we allocate and initialize the memory:

```swift
@_cdecl("Java_com_example_swift_MySwiftClass __00024init__ JJ")
func Java_com_example_swift_MySwiftClass __00024init__ JJ(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, x: jlong, y: jlong) -> jlong {
  let result$ = UnsafeMutablePointer<MySwiftClass>.allocate(capacity: 1)
  result$.initialize(to: MySwiftClass.init(x: Int64(fromJNI: x, in: environment!), y: Int64(fromJNI: y, in: environment!)))
  let resultBits$ = Int64(Int(bitPattern: result$))
  return resultBits$.getJNIValue(in: environment!)
}

```

We are basically allocating memory for a single instance of `MySwiftClass`, initializing it to a new instance and returning the memory address of the pointer. It is the same approach for `struct` as well!

## Destruction

At some point, when the associated `SwiftArena` has decided that the instance must be destroyed and deinitialized it will call a `destroy` method on the instance. Its implementation is source-generated for each instance at looks like this:

```swift
@_cdecl("Java_com_example_swift_MySwiftClass __00024destroy__ J")
func Java_com_example_swift_MySwiftClass __00024destroy__ J(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, selfPointer: jlong) {
  let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))
  guard let self$ = UnsafeMutablePointer<MySwiftClass>(bitPattern: selfBits$) else {
    fatalError("self memory address was null in call to \(#function)!")
  }
  self$.deinitialize(count: 1)
  self$.deallocate()
}

```

We just basically recreate a pointer to the memory address of the instance, deinitialize the memory and finally deallocate!

By using Swift to allocate and deallocate memory, we don’t have to worry about manually managing the reference count and tampering with the witness tables!

# Optionals

An interesting part about the implementation is how we wrap optionals. Java provides the generic `Optional<T>` and specialized types such as `OptionalLong`, `OptionalInt` etc. The first intuition I had for implementing this was basically just passing in these types to Swift (as `jobject)` and using JNI to call methods like `isPresent()` and `get()`, for return values, we could initialize a Java `Optional` on the Swift-side using JNI. However, this sort of back-and-forth between JNI and Swift is pretty bad for performance. Whenever we can, we should try to avoid creating objects etc using JNI.

Instead we ended up doing a small “hack”. When passing in optional parameters to a wrapped Swift function, we provide a boolean value to the native function to indicate whether the optional is present:

```java
public static void takeOptional(OptionalInt i) {
  $takeOptional((byte) i.isPresent(), i.orElse(0));
}

static native void $takeOptional(byte i_discriminator, int i_value);

```

This way we can read the discriminator in Swift to decide whether to pass `nil` or the value to the Swift function:

```swift
@_cdecl("Java_com_example_swift_MySwiftLibrary __00024takeOptional__ BI")
func Java_com_example_swift_MySwiftLibrary __00024takeOptional__ BI(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, i_discriminator: jbyte, i_value: jint) -> jlong {
  MySwiftLibrary.takeOptional(i: input_discriminator == 1 ? Int32(fromJNI: input_value, in: environment!) : nil)
}

```

For returning optionals, we do some different things depending on the type:

- If the type is a primitive and fits inside a bit-wise larger primitive with additional space for 8 bits, we use the last 8 bits for a discriminator and the first X bytes for the value.
- If not, we use an “indirect return” through a byte array to pass back the discriminator.

Here is an example of how this works using an `OptionalInt`, where `int` is stored along with the discriminator inside a `long`.

```swift
@_cdecl("Java_com_example_swift_MySwiftLibrary__00024optionalInt")
func Java_com_example_swift_MySwiftLibrary__00024optionalInt(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass) -> jlong {
  let result_value$ = MySwiftLibrary.optionalInt().map {
    Int64($0) << 32 | Int64(1)
  } ?? 0
  return result_value$.getJNIValue(in: environment!)
}

```

As you can see we are bitshifting the value by `32` bits and storing the a `1` afterwards to indicate non-nil inside the result.  
On the Java side we “unpack” this result and parse it accordingly:

```java
public static OptionalInt optionalInt() {
  long result_combined$ = MySwiftLibrary.$optionalInt();
  byte result_discriminator$ = (byte) (result_combined$ & 0xFF);
  int result_value$ = (int) (result_combined$ >> 32);
  return result_discriminator$ == 1 ? OptionalInt.of(result_value$) : OptionalInt.empty();
}

```

# Enums

Another cool language feature that we got working in JNI mode is enums, **even with associated values.** Enums are extracted as a `class` and each case is its own `record`. This way gives us the most flexibility in terms of matching the enum features available in Swift, such as switch statements with associated values.

Consider the following enum `Vehicle`:

```swift
public enum Vehicle {
  case bicycle
  case car(maker: String)
}

```

The JNI mode exports a Java wrapper roughly defined as:

```java
public final class Vehicle {
  // An interface that describes each case of the enum
  public sealed interface Case {}

  // Returns a Case depending on the underlying Swift case
  public Case getCase() { … }

   // Returns a bicycle case
  public static Vehicle bicycle(SwiftArena swiftArena$) { … }

  // Returns the bicycle values (if the underlying case is a bicycle)
  public Optional<Bicycle> getAsBicycle() { … }
  
  // Returns a car case with the passed in maker
  public static Vehicle car(java.lang.String maker, SwiftArena swiftArena$) { … }

  // Returns the bicycle values (if the underlying case is a bicycle)
  public Optional<Car> getAsCar() { … }

  public record Bicycle() implements Case {}
  public record Car(java.lang.String maker, Optional<String> trailer) {}
}

```

The `getAsX()` method allows you to achieve a similar syntax to the Swift syntax `if case .car(let maker) = vehicle`:

```java
Optional<Vehicle.Car> car = myVehicle.getAsCar();
if(car.isPresent()) {
  System.out.println(“The maker of the car is: “ + car.maker());
} 

```

Using the `getAsCase()` and the JDK 21+ feature [pattern matching for switch](https://openjdk.org/jeps/441) you can get very close to the syntax of a traditional Swift switch-statement:

```java
Vehicle vehicle = ...;
switch (vehicle.getCase()) {
    case Vehicle.Bicycle b:
        System.out.println("I am a bicycle!");
        break
    case Vehicle.Car c:
        System.out.println("Car maker: " + c.maker());
        break
}

```

> For more information about the enum extraction and how to use it, please see the [Enum documentation](https://swiftpackageindex.com/swiftlang/swift-java/main/documentation/swiftjavadocumentation/supportedfeatures#Enums)

## Alternatives considered

One of the first ideas I had for enums was to have the `Vehicle` defined as a sealed `class` and the sub-types defining the cases be a subclass of that. However, since Swift enums can be mutated to another case, this could cause weird behaviour if the underlying type changes case.

Consider the following example:

```swift
public enum Vehicle {
  case car
  case bicycle

  mutating func change() {
    self = .bicycle
  }
}

```

Imagine that it was extracted as three types:

- `sealed class Vehicle`,
- `final class Car extends Vehicle`
- `final class Bicycle extends Vehicle`

Let us now imagine that a developer does the following:

```java
Vehicle myVehicle = Vehicle.car();
Car car = myVehicle.getAsCar().get();
myVehicle.upgrade();
String maker = car.getMaker();

```

The last line would result in a runtime crash, as the underlying Swift instance would have changed to a `bicycle` and therefore no longer be a `Car`. Therefore, we do not expose cases as subclasses but rather as just basic copies of the associated values.

# Future steps

There is still a ton of improvement to be done and language features to be added! We would like to encourage anyone interested to pick up any of these and contribute to the project!

Here is an incomplete list of things we would like to do at some point:

- Bring FFM up-to-date with the features added in JNI
- Add support for `async` methods
- Add support for `@escaping` closures for callbacks etc.
- Figure out a plan for how we distribute and wrap entire Swift libraries
- Add support for `actor`
- Add support for typed throws `throws(MyError)`
- Support for dictionaries and arbitrary arrays.
- Tuples in JNI mode.
- Default parameters
- Custom operators `+, -`
- Inheritance
- Performance improvements
- Adding type-specific extraction configuration using macros, such as `@jextract(.ignore), @jextract(.noCopy)`
- _and a bunch more…_

These are just some of the ideas we have! Feel free to reach out and we’d be happy to collaborate on some of these! Or anything else interesting

# My experience with GSoC

Google Summer of Code was an awesome experience for me! I got to work with my favourite language on a library that is very relevant! A **HUGE** thanks to my mentor @ktoso, who provided invaluable guidance, was always available for questions and allowed me to experiment and take ownership of the work!

I would definitely recommend GSoC to anyone interested, it is a great way to engage with the open-source community, develop your skills and work with some talented people! My number one advice would be to never be afraid of asking seemingly “stupid” questions, they might not be that stupid afterall:)

# Merged pull requests

- [[JExtract/JNI] Add support for protocols as parameter types #376](https://github.com/swiftlang/swift-java/pull/376)
- [Add "..." wildcard syntax to output assertions in JExtract tests #362](https://github.com/swiftlang/swift-java/pull/362)
- [[JExtract/JNI] Add auto arena to SwiftKitCore and add memory management options #353](https://github.com/swiftlang/swift-java/pull/353)
- [[JExtract/JNI] Import enums #346](https://github.com/swiftlang/swift-java/pull/346)
- [[JExtract/JNI] Support JavaKit wrapped types as function parameters. #330](https://github.com/swiftlang/swift-java/pull/330)
- [[JExtract/JNI] Add support for primitive non-escaping closures. #327](https://github.com/swiftlang/swift-java/pull/327)
- [[JExtract/JNI] Add support for classes/structs as parameters and return values #326](https://github.com/swiftlang/swift-java/pull/326)
- [[JExtract/JNI] Fix boolean naming for variables already called isX #325](https://github.com/swiftlang/swift-java/pull/325)
- [[JavaKit] Conform Int to JavaValue #317](https://github.com/swiftlang/swift-java/pull/317)
- [[JExtract/JNI] Add tests for structs #316](https://github.com/swiftlang/swift-java/pull/316)
- [[JExtract/JNI] Omit unsupported types instead of crashing #313](https://github.com/swiftlang/swift-java/pull/313)
- [[JExtract/JNI] Add support for variables #306](https://github.com/swiftlang/swift-java/pull/306)
- [[JExtract] Add support for JNI member methods #305](https://github.com/swiftlang/swift-java/pull/305)
- [[JExtract] Generate JNI code for memory management with SwiftKitCore #302](https://github.com/swiftlang/swift-java/pull/302)
- [Separate SwiftKit into SwiftKitCore and SwiftKitFFM #300](https://github.com/swiftlang/swift-java/pull/300)
- [​​[JExtract] Generate JNI code for classes (static methods and initializers) #298](https://github.com/swiftlang/swift-java/pull/298)
- [Add JExtract JNI sample app #295](https://github.com/swiftlang/swift-java/pull/295)
- [[jextract] add support for throwing functions in JNI mode #277](https://github.com/swiftlang/swift-java/pull/277)
- [[jextract] add support for strings in JNI #273](https://github.com/swiftlang/swift-java/pull/273)
- [[jextract] Generate code for free functions with primitive types in JNI mode #269](https://github.com/swiftlang/swift-java/pull/269)
- [Introduce protocol-based structure for generating code #249](https://github.com/swiftlang/swift-java/pull/249)

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 29, 2025, 1:22am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/2 "2025-08-29T01:22:08Z")

</div>

Really fantastic work, Mads! We went from zero, to implementing novel features with extremely nice usability and optimized performance (as much as JNI allowed). Really proud of the work and I hope we’ll be able to continue work on it even after GSoC concludes!

If anyone sees this post and is thinking to themselfes they’d like to participate and help improve the list of supported features either in ffm (server focus) or jni (android focus), please don’t hesitate to reach out!

---

<div class="post-metadata">

**Author:** ![grynspan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/grynspan/32/15315_2.png) [@grynspan](https://forums.swift.org/u/grynspan)\
**Post date:** [August 29, 2025, 4:32am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/3 "2025-08-29T04:32:43Z")

</div>

This is great to see!

---

<div class="post-metadata">

**Author:** ![Honza\_Dvorsky](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/honza_dvorsky/32/29867_2.png) [@Honza\_Dvorsky](https://forums.swift.org/u/Honza_Dvorsky)\
**Post date:** [August 29, 2025, 6:47am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/4 "2025-08-29T06:47:30Z")

</div>

Great work, @madsodgaard - lots to be proud of! 👏

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 29, 2025, 7:14am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/5 "2025-08-29T07:14:04Z")

</div>

I noticed we don’t call this out explicitly in samples, but it’s really cool what we did with records, you can properly pattern match over “swift enums” the same way in Java:

```java
try (var arena = SwiftArena.ofConfined()) {
    Vehicle vehicle = Vehicle.car("BMW", Optional.empty(), arena);
    switch (vehicle.getCase(arena)) {
        case Vehicle.Bicycle b:
            break;
        case Vehicle.Car(var name, var something):
            break;
   }
}

```

Thought that’s worth calling out, will [add it to docs as well](https://github.com/swiftlang/swift-java/pull/381) 😃

---

<div class="post-metadata">

**Author:** ![grynspan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/grynspan/32/15315_2.png) [@grynspan](https://forums.swift.org/u/grynspan)\
**Post date:** [August 29, 2025, 9:03pm UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/6 "2025-08-29T21:03:29Z")

</div>

I have some optimization thoughts I'd like to share. Would GitHub issues be your preferred way for me to do so?

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 30, 2025, 12:50am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/7 "2025-08-30T00:50:43Z")

</div>

Depends if they need a lot of discussion and are not fully formed ideas (forums), or if they’re somewhat actionable already (GitHub) 🙂

Thanks in advance

---

<div class="post-metadata">

**Author:** ![Demolino](https://avatars.discourse-cdn.com/v4/letter/d/839c29/32.png) [@Demolino](https://forums.swift.org/u/Demolino)\
**Post date:** [October 28, 2025, 5:53am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/8 "2025-10-28T05:53:47Z")

</div>

hey, just found this, and im curries if this allowed Java Dev's calling MacOS System API that's are in the Swift Code? or dos this only work with own Swift Project? Would be nice to be enables to write something like this in Java…

> **[GitHub - nikitabobko/AeroSpace: AeroSpace is an i3-like tiling window manager for...](https://github.com/nikitabobko/AeroSpace)**
>
> AeroSpace is an i3-like tiling window manager for macOS

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [October 28, 2025, 8:30am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/9 "2025-10-28T08:30:31Z")

</div>

The intent of this tool is to allow calling any Swift library from Java through source generated bindings. There's no real limitations what those libraries are.

The swift-java project supports interop in both directions, swift-\>java and java-\>swift.

---

<div class="post-metadata">

**Author:** ![Demolino](https://avatars.discourse-cdn.com/v4/letter/d/839c29/32.png) [@Demolino](https://forums.swift.org/u/Demolino)\
**Post date:** [October 30, 2025, 10:11am UTC](https://forums.swift.org/t/gsoc-2025-new-jni-mode-added-to-swift-java-jextract-tool/81858/10 "2025-10-30T10:11:40Z")

</div>

cool, looking forward to see more about this! thanks
