Add dart/flutter bindings to livekit-uniffi - #1183
Conversation
ChangesetThe following package versions will be affected by this PR:
|
cdd271c to
c60ae51
Compare
1egoman
left a comment
There was a problem hiding this comment.
A few high level thoughts. I am deferring an official review approval to folks with more first hand flutter experience.
| if (zipResp.statusCode != 200) { | ||
| throw Exception('Failed to download $zipUrl: HTTP ${zipResp.statusCode}'); | ||
| } |
There was a problem hiding this comment.
nitpick: Also in the aim of future flexibility, maybe making this verify 2xx not strictly 200 might be a good idea.
| output.assets.code.add( | ||
| CodeAsset( | ||
| package: input.packageName, | ||
| // Dart prefixes this with `package:<packageName>/`. | ||
| name: 'uniffi:$_cdylibName', | ||
| linkMode: DynamicLoadingBundled(), | ||
| file: libFile, | ||
| ), | ||
| ); |
There was a problem hiding this comment.
thought: An interesting related blog post I came across in my research: https://www.simonbinder.eu/posts/native_assets/
I'm not sure how much of this applies since this is a net new package, but the thing that stood out to me was that it seems like by using native assets (I think that's what you are doing here?) you potentially force consumers to also enable the native assets feature. Maybe that's fine but could be problematic once this gets folded into the dart sdk and could maybe break existing users
(Feel free to ignore this if it's not relevant, you're probably done more research into dart packaging than I have at this point)
There was a problem hiding this comment.
As we discussed (but duplicated here for posterity):
uniffi-dartgenerates Native Assets bindings, anduniffi-bindgen-dartgenerates DynamicLibrary based bindings.- I think this would mean changing the bindings, i.e. changing upstream bindgens.
- @hiroshihorie expressed no strong opinion about which flavour of bindings to support.
Additionally:
- this post dates from when Native Assets were experimental, 2024-early 2025; it was the flipping of the
--enable-experiment=native-assetsflag's virality that was a concern for this post. - Native Assets are now the blessed path forward, gated on dart version 3.10 / flutter 3.38
livekit/client-sdk-flutter specifies min versions: 3.60 / 3.27 so this would cause churn for users when we migrate from a pure-dart to a Rust based SDK. Checking with @hiroshihorie for advice.
131677b to
aa17b5b
Compare
| dependencies: | ||
| ffi: ^2.1.0 | ||
|
|
||
| dev_dependencies: | ||
| # Native Assets build-hook support. `any` tracks the SDK-bundled versions. | ||
| code_assets: any | ||
| hooks: any | ||
| # Used by hook/build.dart to download and verify prebuilt libraries. | ||
| archive: ^4.0.0 | ||
| crypto: ^3.0.0 | ||
| http: ^1.2.0 | ||
| test: ^1.25.0 |
There was a problem hiding this comment.
Since consumers compile and run hook/build.dart at their own build time, its imports need to be regular dependencies. A dependency's dev_dependencies are not resolved for downstream apps, so as written the hook fails to compile once an app depends on livekit_uniffi (the hooks doc calls this out). It works in-repo because the package is the root package there. I reproduced both the failure and the fix with a small path-dependency consumer.
| dependencies: | |
| ffi: ^2.1.0 | |
| dev_dependencies: | |
| # Native Assets build-hook support. `any` tracks the SDK-bundled versions. | |
| code_assets: any | |
| hooks: any | |
| # Used by hook/build.dart to download and verify prebuilt libraries. | |
| archive: ^4.0.0 | |
| crypto: ^3.0.0 | |
| http: ^1.2.0 | |
| test: ^1.25.0 | |
| dependencies: | |
| ffi: ^2.1.0 | |
| # Native Assets build-hook support. `any` tracks the SDK-bundled versions. | |
| code_assets: any | |
| hooks: any | |
| # Used by hook/build.dart to download and verify prebuilt libraries. | |
| archive: ^4.0.0 | |
| crypto: ^3.0.0 | |
| http: ^1.2.0 | |
| dev_dependencies: | |
| test: ^1.25.0 |
test is the only one that is genuinely dev-only. 🙂
There was a problem hiding this comment.
Ok, everything else to dependencies.
| if (arch == Architecture.x64) return 'x86_64-linux-android'; | ||
| } else if (os == OS.iOS) { | ||
| if (arch == Architecture.arm64) return 'aarch64-apple-ios'; | ||
| } |
There was a problem hiding this comment.
thought: Is iOS Simulator in scope here? _targetTriple maps iOS + arm64 straight to the device triple aarch64-apple-ios without checking code.iOS.targetSdk, and the workflow matrix builds no simulator artifacts (aarch64-apple-ios-sim, x86_64-apple-ios). So a simulator run on an Apple Silicon Mac downloads the device dylib and fails later in the Xcode link step with a Mach-O platform mismatch ("building for iOS Simulator, but linking in dylib built for iOS"), while an Intel Mac throws UnsupportedError.
If simulator support is intended, the fix is a targetSdk check in the hook plus the two simulator targets in the matrix. If device-only is intentional for this PR, a guard like this would turn the confusing late link error into a clear one:
if (code.iOS.targetSdk == IOSSdk.iPhoneSimulator) {
throw UnsupportedError('iOS Simulator is not supported yet');
}Either way works, mostly flagging it since the matrix building aarch64-apple-ios makes iOS look fully supported.
There was a problem hiding this comment.
Added support for the simulator.
| [tasks.bindgen] | ||
| dependencies = [ | ||
| "bindgen-kotlin", | ||
| "bindgen-python" | ||
| "bindgen-python", | ||
| "bindgen-dart", | ||
| ] |
There was a problem hiding this comment.
🔍 Adding bindgen-dart to the shared bindgen task changes existing behavior
The bindgen task at livekit-uniffi/Makefile.toml:167-172 now includes bindgen-dart alongside bindgen-kotlin and bindgen-python. Before this PR, cargo make bindgen only generated Kotlin and Python bindings. Now it will also attempt Dart generation, which requires fetching and compiling the uniffi-dart git dependency (gated behind the dart-bindgen feature). This adds significant compile time to an existing workflow. Developers who only need Kotlin/Python bindings will pay this cost. Consider whether bindgen-dart should be opt-in (a separate bindgen-all task) rather than added to the default bindgen task.
Was this helpful? React with 👍 or 👎 to provide feedback.
Release/multi-platform delivery for the Dart package: - uniffi-cdylib.yml builds liblivekit_uniffi for desktop + mobile targets and attaches build-<triple>.zip (+ .sha256) to the livekit-uniffi release; wired into uniffi-packages.yml. Validated across all 10 targets via CI dry-run. - hook/build.dart.tera gains a download mode: fetches and SHA-256-verifies the prebuilt library for the target when no local build is present.
Ship tests in support/dart/test, copied into the generated package by the dart-package flow. Covers the FFI smoke path (buildVersion) and a real JWT/HMAC round-trip (generate -> verify -> decode, plus wrong-secret rejection) to exercise Rust logic across the boundary.
uniffi-dart-test.yml builds the Dart package (host cdylib + bindings via cargo make dart-package) and runs dart test on PRs/pushes touching livekit-uniffi or its dependencies. The dart build keeps symbols (CARGO_PROFILE_RELEASE_STRIP=false) so library-mode bindgen can read the UNIFFI_META_* metadata, which the release strip removes on Linux.
…p-slip, local-mode caching - uniffi-packages.yml: grant contents:write to the cdylib job so its release upload isn't capped by the caller's read-only token (would 403 on release). - Makefile.toml: drive bindgen-dart through run_task so CARGO_PROFILE_RELEASE_STRIP is in scope while the build dependency compiles (else metadata is stripped on Linux). - build.dart.tera: reject zip entries that escape the output dir (zip-slip); register the local-mode lib in output.dependencies so a refreshed dylib isn't masked by a cached hook result; fix stale asset-name comment.
8a1dc4f to
b937d5f
Compare
Before you submit your PR
Make sure the following is true before submitting your PR:
PR description
Added Dart bindings with the uniffi-dart bindgen. This generates a
Native AssetsAPI. Exercise withcargo make bindgen-dart.This adds a uniffi-bindgen-dart feature to the
livekit-unifficrate: followup would be to separate out the uniffi-tooling binaries in to a workspace centred tooling crate.Added consumable package, with a hook/build.dart. This works both locally, and by downloading a prebuilt cdylib.
Added CI which builds the cdylibs per OS/architecture on release.
Breaking changes
No changes breaking changes are made, however:
uniffi-dartbindgen needed a bug fix which means the version of uniffi-dart is from main (pinned to the hash).uniffi-bindgen-node. Followup: moveuniffi-bindgen-nodetouniffi-bindgen-react-native.MSRV
No changes. uniffi 0.31 requires >= 1.85
Testing
support/dart/tests): a smoke test including a negative case.Async
We want the project to be runtime-agnostic, so please reuse what's already in livekit-runtime and feel free to add anything missing. It's ok to use Tokio directly, when writing unit tests, if necessary. When testing, do not use artificial delays for the state to "catch up"; instead, respect the event flow and subscribe properly using channels or other mechanisms.