IOS Release running error: Failed to lookup symbol
When I compile and run in deubg mode on ios, everything works fine. But when I switch to release, I got the error like below:
2021-01-26 20:36:03.466780+0800 Runner[26430:9877639] Metal API Validation Enabled
2021-01-26 20:36:03.669728+0800 Runner[26430:9877805] [VERBOSE-2:ui_dart_state.cc(177)] Unhandled Exception: Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, store_dart_post_cobject): symbol not found)
#0 DynamicLibrary.lookup (dart:ffi-patch/ffi_dynamic_library_patch.dart:31)
#1 _store_dart_post_cobject (package:full_search/ffi.dart)
#2 _store_dart_post_cobject (package:full_search/ffi.dart)
#3 store_dart_post_cobject (package:full_search/ffi.dart:263)
#4 SearchEngine.setup (package:full_search/full_search.dart:12)
#5 _MyAppState.initSearchEngine (package:full_search_example/main.dart:38)
#6 _MyAppState.initState (package:full_search_example/main.dart:31)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4822)
#8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4659)
#9 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3625)
#10 Element.updateChild (package:flutter/src/widgets/framework.dart:3390)
#11 RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:1208)
#12 RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:1179)
#13 RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:1121)
#14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2731)
#15 RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:1120)
#16 WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:960)
#17 WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (package:flutter/src/widgets/binding.dart:941)
#18 _rootRun (dart:async/zone.dart:1178)
#19 _CustomZone.run (dart:async/zone.dart:1090)
#20 _CustomZone.runGuarded (dart:async/zone.dart:994)
#21 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1034)
#22 _rootRun (dart:async/zone.dart:1186)
#23 _CustomZone.run (dart:async/zone.dart:1090)
#24 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1018)
#25 TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart:399)
#26 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395)
#27 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426)
#28 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184)
you need to include allo-isolate crate.
or that means that Xcode tree-shaking process decided to strip your static library.
to solve this issue, there is a workaround this to force Xcode Link to your library.
what should do is creating a no-op function let's call it link_me_please something like this
#[no_mangle]
pub extern "C" fn link_me_please() {}
and then in your package/ios add a dommy method in your class that calls this function. here is an example from this repo: https://github.com/shekohex/flutterust/blob/master/packages/adder_ffi/ios/Classes/SwiftAdderPlugin.swift#L13
note that your Makefile.toml the cargo make script should also copy the binding.h (the generated header file) to Classes folder .. which I guess is set to that by default.
Right now, I did like this
https://github.com/shekohex/flutterust/blob/master/packages/scrap_ffi/ios/Classes/SwiftScrapPlugin.swift
public static func dummyMethodToEnforceBundling() {
last_error_length()
}
It works well in ios debug mode, async function works well too.
And I found a releated issue in another project. https://github.com/brickpop/flutter-rust-ffi/issues/7
I'm sorry to be late on this, but where the readme says add a dummy method in SwiftMylibPlugin.swift that uses at least one of the native functions, it should say add a dummy call for every single one of them.
For some unexplainable reason:
In Debug mode, XCode will bundle the entire library if at least one library function is used. In Release mode, XCode will do tree shaking, and only bundle the native functions and the callees that you explicitly reference I learnt it the hard way myself, I'm updating the Readme right now.
And I'm confused. There is no store_dart_post_cobject function in binding.h, how can it be called? And it work fine in IOS debug mode.
the store_dart_post_cobject is function exported from allo-isolate package.
so one way to make it exported is to re-export it again or use something like this:
use allo_isolate::ffi;
#[no_mangle]
pub unsafe extern "C" fn store_dart_post_cobject(
ptr: ffi::DartPostCObjectFnType,
) {
allo_isolate::store_dart_post_cobject(ptr);
}
Also worth mentioning this too: https://flutter.dev/docs/development/platform-integration/c-interop#ios-symbols-stripped
iOS symbols stripped
When creating a release archive (IPA) the symbols are stripped by Xcode.
In Xcode, go to Target Runner > Build Settings > Strip Style.
Change from All Symbols to Non-Global Symbols.
After did like above.
This time, I am trying to build and run flutterust project directly in IOS release mode. Also facing the same error.
2021-01-27 10:22:42.684201+0800 Runner[27043:10106244] Metal API Validation Enabled
2021-01-27 10:22:43.004687+0800 Runner[27043:10106378] flutter: Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, store_dart_post_cobject): symbol not found)
2021-01-27 10:22:43.008625+0800 Runner[27043:10106378] flutter: #0 DynamicLibrary.lookup (dart:ffi-patch/ffi_dynamic_library_patch.dart:31)
2021-01-27 10:22:43.008682+0800 Runner[27043:10106378] flutter: #1 _store_dart_post_cobject (package:scrap/ffi.dart)
2021-01-27 10:22:43.008705+0800 Runner[27043:10106378] flutter: #2 _store_dart_post_cobject (package:scrap/ffi.dart)
2021-01-27 10:22:43.008723+0800 Runner[27043:10106378] flutter: #3 store_dart_post_cobject (package:scrap/ffi.dart:63)
2021-01-27 10:22:43.008741+0800 Runner[27043:10106378] flutter: #4 Scrap.setup (package:scrap/scrap.dart:10)
2021-01-27 10:22:43.008760+0800 Runner[27043:10106378] flutter: #5 _MyHomePageState.initState (package:flutterust/main.dart:37)
2021-01-27 10:22:43.008776+0800 Runner[27043:10106378] flutter: #6 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4822)
Unfortunately, I don't have access to a macOS Machine right now to do tests and try to fix this issue, but feel free if you got it to work correctly to Open a PR for README to add steps to fix this issue. Thanks :)
You can pull this project from me and it has been resolved. https://github.com/Aaron009/flutter-rust-ffi
the reason: https://github.com/brickpop/flutter-rust-ffi/pull/23
I separated a plugin for just the basics like lazy initialization of the RUNTIME and linking of the store_dart_post_cobject symbol. In the Swift plugin header, I have added a declaration
In packages/runtime_ffi/ios/Classes/RuntimePlugin.h:
#import <Flutter/Flutter.h>
void* store_dart_post_cobject(void*);
@interface RuntimePlugin : NSObject<FlutterPlugin>
@end
In a static function that I don't call, I call all of the methods that I would like linked.
In packages/runtime_ffi/ios/SwiftRuntimePlugin.swift:
import Flutter
import UIKit
public class SwiftRuntimePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "runtime", binaryMessenger: registrar.messenger())
let instance = SwiftRuntimePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
public static func dummyMethodToEnforceBundling() {
store_dart_post_cobject(nil)
runtime_setup()
last_error_length()
error_message_utf8(nil, 0)
}
}
This is all with Non-Global Symbols set for iOS

I believe that any other symbols that could have this issue would be in Rust code that you add. Those symbols should be trivial to find and add to dummyMethodToEnforceBundling for each package plugin.
I found that even though this allows the build to succeed, this is not a fix. The POST_COBJECT is never stored correctly and is always None during post calls even after setup.