Skip to content

[pigeon] Add FFI and JNI support to Swift and Kotlin - #11352

Open
tarrinneal wants to merge 176 commits into
flutter:mainfrom
tarrinneal:Kamyshin
Open

[pigeon] Add FFI and JNI support to Swift and Kotlin#11352
tarrinneal wants to merge 176 commits into
flutter:mainfrom
tarrinneal:Kamyshin

Conversation

@tarrinneal

@tarrinneal tarrinneal commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

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

@stuartmorgan-g stuartmorgan-g left a comment

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.

Some code nits, but mostly just docs feedback at this point!

Comment thread packages/pigeon/example/native_interop_app/ios/Runner/AppDelegate.swift Outdated
Comment thread packages/pigeon/example/native_interop_app/pigeons/native_interop_example.dart Outdated
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon, do not edit directly.

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.

Nit: maybe generate a blank link above this so it's easier to see that this isn't part of the boilerplate header?

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.

I'll do a follow up pr to change all the generators since this applies to all of them

Comment thread packages/pigeon/README.md Outdated
Comment thread packages/pigeon/README.md Outdated
Comment thread packages/pigeon/README.md Outdated
Comment thread packages/pigeon/README.md Outdated
Comment thread packages/pigeon/README.md
- **Consider Native Interop if**:
- Your plugin targets only Android, iOS, and/or macOS.
- Your plugin handles high-frequency messaging, large typed arrays (e.g., image processing, sensor data streams), or latency-sensitive communication where serialization overhead is a bottleneck.
- You need synchronous execution for platform APIs on the host thread.

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 think we'll need to refine this, since currently it sounds like we don't think most people should consider native interop, but we can revisit that in follow-ups as part of the docs updates.

@tarrinneal

Copy link
Copy Markdown
Contributor Author

@stuartmorgan-g This last commit is worth taking a look at (the threading/isolate docs mostly) 77be3d4

@tarrinneal

Copy link
Copy Markdown
Contributor Author

@stuartmorgan-g This last commit is worth taking a look at (the threading/isolate docs mostly) 77be3d4

I made that commit before reading your feedback, seems like most of the things you mentioned I had already changed. I still think it's worth a glance since my isolate work is minimal.

@stuartmorgan-g stuartmorgan-g left a comment

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.

Small nits, but LGTM

@@ -73,7 +67,11 @@ In your Pigeon Dart definition file (`pigeons/<messages_file>.dart`), update `@C
- **`appDirectory`**: Root path of the compiled Flutter **application** context required by `ffigen` and `jnigen` (use `'example/'` for plugins with an example app, or `'./'` for standalone Flutter apps).

> [!WARNING]
> **Threading & TaskQueue**: Native Interop (FFI/JNI) calls execute directly in-process and always run on the main UI thread. `@TaskQueue` annotations are not supported with Native Interop and must be removed from your Pigeon file before generating code.
> **Threading, Isolates & TaskQueue**: Native Interop calls execute directly in-process on the calling isolate's OS thread (the main UI thread when called from the root isolate).

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.

Remove "in-process"

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.

this is a great example of why skills are problematic in design

Comment thread packages/pigeon/skills/native-interop-migration/SKILL.md Outdated
Comment thread packages/pigeon/skills/native-interop-migration/SKILL.md Outdated
Comment thread packages/pigeon/skills/native-interop-migration/SKILL.md Outdated
Comment thread packages/pigeon/native_interop_guide.md Outdated
Comment thread packages/pigeon/native_interop_guide.md Outdated
Comment thread packages/pigeon/native_interop_migration_guide.md Outdated
Comment thread packages/pigeon/README.md Outdated
Pigeon removes the necessity to manage strings across multiple platforms and languages.
It also improves efficiency over common method channel patterns. Most importantly though,
It also improves efficiency over common platform channel patterns. Most importantly though,
it removes the need to write custom platform channel code, since pigeon generates it for you.

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.

How so; isn't the whole point of the FFI backend of Pigeon that clients don't have to write their own code translating from *gen code to idiomatic Dart code?

Right now, the docs say that the "most import" thing Pigeon does is something that doesn't apply when using the FFI backend, so it's not at all clear from reading this paragraph what FFI Pigeon is offering clients over using *gen directly, which is why I think we need to broaden the language here.

@tarrinneal tarrinneal mentioned this pull request Sep 1, 2026
@tarrinneal

Copy link
Copy Markdown
Contributor Author

currently blocked by #12708

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.

3 participants