fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.

Home Page:https://fzyzcjy.github.io/flutter_rust_bridge/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Method hangs on iOS when another dependency that uses flutter rust bridge exists

roeierez opened this issue · comments

Describe the bug

We use flutter rust bridge in breez: https://github.com/breez.
The plugin is deployed to a different repo: https://github.com/breez/breez-sdk-flutter
When used alone in a flutter mobile app everything works as expected. But when we add bdk_flutter (https://pub.dev/packages/bdk_flutter) which also uses flutter rust bridge, on iOS only, every method hangs.

Steps to reproduce

  1. Create a blank flutter project
  2. Add these dependencies:
breez_sdk:
  git:
    ref: v0.2.15
    url: https://github.com/breez/breez-sdk-flutter.git
bdk_flutter: ^0.30.0
  1. Add the following main.dart code:
import 'package:flutter/material.dart';
import 'package:breez_sdk/breez_sdk.dart';

void main() async {
  final breezSDK = BreezSDK();
  await breezSDK.mnemonicToSeed("abandon abandon abandon abando");
  runApp(const MyApp());
}

The second line in main just hangs.
If we remove the bdk_flutter dependency then it works again.
Happy to provide any information necesary.

Logs

Logs:

Expected behavior

No response

Generated binding code

No response

OS

iOS

Version of flutter_rust_bridge_codegen

No response

Flutter info

No response

Version of clang++

No response

Additional context

No response

Hi! Thanks for opening your first issue here! 😄

Hi, IIRC if you use static library (DynamicLibrary.process() instead of DynamicLibrary.open(...)) on ios with multiple packages, this will happen, because multiple rust libraries have same symbol names thus have a conflict.

More details if you are interested, e.g. shekohex/allo-isolate#55 (flutter_rust_bridge uses allo-isolate)

Therefore, firstly, this is not a problem in v2, because the DynamicLibrary.open is used (thus no such conflict even on ios). Secondly, if you are still using v1, you can change a few lines of code about DynamicLibrary.process (e.g. mimic the v2 code https://github.com/fzyzcjy/flutter_rust_bridge/blob/345c3f5fc74dbe85a82c8b4fa0afa4781a453454/frb_dart/lib/src/loader/_io.dart#L260) and it should work.

Thank you. I am on this. Will update here how it goes.

You are welcome and take your time!

if you are still using v1, you can change a few lines of code about DynamicLibrary.process (e.g. mimic the v2 code https://github.com/fzyzcjy/flutter_rust_bridge/blob/345c3f5fc74dbe85a82c8b4fa0afa4781a453454/frb_dart/lib/src/loader/_io.dart#L260) and it should work.

I think this is the wrong link? I don't see line 260.

ExternalLibrary loadExternalLibraryRaw({

(26, not 260, maybe I mistyped a 0 when I wanted to type ))

@fzyzcjy from my understanding the new code attempts to load the symbols from the framework directly? Is it allowed/supported by apple in iOS?
Anyway I tried to DynamicLibrary.load('$libName.framework/$libName') and it didn't work. Also looking at the search path printed by the loader, indeed I didn't see any framework related to breez_sdk in there.
Perhaps there is a specific way of building/linking that this pattern requires? As you can see in our plugin: https://github.com/breez/breez-sdk-flutter/blob/main/ios/breez_sdk.podspec The binaries are linked via BreezSDK pod and not embedded directly in the plugin.

OK removing that line about static linking and now I am able to use DynamicLibrary.open to load the symbols and it works side by side with the bdk_flutter.
Thanks a lot @fzyzcjy for all your help.
I guess the only question in my mind now is what is apple's policy about this. I don't think I ever saw this is explicitly supported anywhere.

I was also surprised the open is used. But interestingly, this is what the latest default flutter template provides:
https://github.com/flutter/flutter/blob/8b6277e63868c2029f1e2327879b7899be44fbe2/packages/flutter_tools/templates/plugin_ffi/lib/projectName.dart.tmpl#L47-L58

@fzyzcjy my findings above were during development in my local machine but in production we have two pods that depends on each other:
breez_sdkFFI - that bindary that contains a verndored_framework with the rust built code.
BreezSDK - that contains the swift files and depends on breez_sdkFFI

When I remove the s.static_framework = true it doesn't work actually because the cocoapods validation complains that the breez_sdkFFI dependency is a static dependency:

ERROR | [iOS] unknown: Encountered an unknown error (The 'Pods-App' target has transitive dependencies that include statically linked binaries: (/private/var/folders/14/hgs_fjmn5ms001tb8qtxxn5c0000gn/T/CocoaPods-Lint-20240131-46442-zk03u2-BreezSDK/Pods/breez_sdkFFI/breez_sdkFFI.xcframework)) during validation.

I am still trying to look for a way to link it dynamically.
I tried also to go in a different direction and look for the problematic dependency and it turns out both plugins use the same version of allo-isolate in rust so I think that shouldn't be the issue here. Perhaps you know a good way to find the problematic dependency so we can at least mitigate this until we can find a sustainable solution?
Thanks a lot for all your help.

@roeierez

I am still trying to look for a way to link it dynamically.

That also looks OK, since if it is dynamically linked, then I guess it is also DynamicLibrary.open, thus no problem.

I tried also to go in a different direction and look for the problematic dependency and it turns out both plugins use the same version of allo-isolate in rust so I think that shouldn't be the issue here.
Perhaps you know a good way to find the problematic dependency so we can at least mitigate this until we can find a sustainable solution?

Not quite get it... Seems you mention only one crate with Rust code. Could you please elaborate a bit more?