Skip to content

Add FFI and JNI support to Swift and Kotlin#11352

Open
tarrinneal wants to merge 102 commits intoflutter:mainfrom
tarrinneal:Kamyshin
Open

Add FFI and JNI support to Swift and Kotlin#11352
tarrinneal wants to merge 102 commits intoflutter:mainfrom
tarrinneal:Kamyshin

Conversation

@tarrinneal
Copy link
Copy Markdown
Contributor

@tarrinneal tarrinneal commented Mar 25, 2026

This PR introduces optional Native Interop to Pigeon, enabling direct communication between Dart and native code without the overhead of traditional MethodChannel serialization. It leverages FFI (Foreign Function Interface) for Swift (iOS/macOS) and JNI (Java Native Interface) for Kotlin (Android).

This represents a significant architectural shift, moving from message-based passing to direct memory sharing and function calls. It also updates the concurrency model for asynchronous methods, moving from completion handlers/callbacks to modern language features: async/await in Swift and Coroutines in Kotlin.

Generators Covered

Swift Generator: Updated to support FFI bindings and async/await for asynchronous methods.
Kotlin Generator: Updated to support JNI bindings and Kotlin Coroutines for asynchronous methods.
Dart Generator: Updated to handle the generated interop bindings on the Dart side.

What's In Scope

  1. Infrastructure: Added core support for useFfi and useJni options.
  2. Automation: Implemented multi-step generation flows that automatically invoke jnigen and ffigen to produce final bindings.
  3. Config Generation: Added jnigen_config_generator.dart and ffigen_config_generator.dart to generate the necessary configuration files for the external tools.
  4. Documentation: Added a detailed native_interop_guide.md explaining prerequisites, setup, and usage.
    Tests: Added ni_tests.dart and associated generated files and integration tests to verify the feature.

What's Out of Scope

  1. Other Languages: This PR specifically targets Swift and Kotlin for the Native Interop feature. Support for Objective-C, C++, and GObject is not included in this interop implementation, and may not be in the future.
  2. Performance Optimization for Complex Classes: As noted in the guide, there is a known performance regression when transferring complex classes with many fields compared to MethodChannel Pigeon. This PR delivers the functional infrastructure, but optimizing this specific case is left for follow-up work.
  3. Non-instant released data. Currently all data that is sent over host or flutter api surfaces is converted to the correct shape and type for the language it is moving toward and the data created in the other language is then discarded. This presents some inefficiencies and potential workflows that are not yet available.

work toward flutter/flutter#182230
design doc flutter/flutter#181430

@tarrinneal tarrinneal added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 31, 2026
@tarrinneal tarrinneal added CICD Run CI/CD and removed CICD Run CI/CD labels Apr 2, 2026
@github-actions github-actions Bot added CICD Run CI/CD and removed CICD Run CI/CD platform-linux platform-windows labels Apr 9, 2026
Copy link
Copy Markdown
Collaborator

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very early review notes; since it's going to be a long review I thought I should post things incrementally. So far I've mostly just looked at the smaller bits, not the full generators yet (and not the tests).

Please also add a real PR description, with issue references and a high level overview of the PR (what it does, what generators it covers, what's in and out of scope here, etc.)

return true
}

return innerValue is NSNull
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for making this method so verbose instead of just ||ing the condition onto the existing statement?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check if case Optional<Any>.some(Optional<Any>.none) = value uses pattern matching. This is a control flow statement, not a boolean expression so it can't be used with logical ||

Comment thread packages/pigeon/example/app/lib/src/event_channel_messages.g.dart
Comment thread packages/pigeon/lib/src/ast.dart
}

/// Wraps provided [toWrap] with [start] and [end] if [wrap] is true.
String wrapConditionally(String toWrap, String start, String end, bool wrap) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is so that toWrap can be an expression, without having to hoist that expression into a local variable to be able to use the statement below?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, It can be worse than that too. This can resolve some manual indentation that has been happening.

Comment thread packages/pigeon/lib/src/generator_tools.dart Outdated
Comment thread packages/pigeon/lib/src/pigeon_lib.dart Outdated
Comment thread packages/pigeon/lib/src/pigeon_lib.dart Outdated
Comment thread packages/pigeon/lib/src/pigeon_lib.dart
Comment thread packages/pigeon/lib/src/pigeon_lib_internal.dart Outdated
Comment thread packages/pigeon/lib/src/pigeon_lib_internal.dart Outdated
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants