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
- Create a blank flutter project
- Add these dependencies:
breez_sdk:
git:
ref: v0.2.15
url: https://github.com/breez/breez-sdk-flutter.git
bdk_flutter: ^0.30.0
- 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.
(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.
Perhaps because the podspec defines static linking? https://github.com/breez/breez-sdk-flutter/blob/main/ios/breez_sdk.podspec#L17
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.
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?