So my flutter app's isolate function is waking up in the background due to iOS's delegate method didUpdateLocation getting called.
I am able to see print logs on the flutter side when didUpdateLocation gets called, but I am getting a missing plugin exception when I try to use path_provider.
I believe my issue is I am not registering the Applications plugins with the Flutter Engine that gets spawned in didUpdateLocations
Note: This location logic on the native iOS side is currently in the Main Flutter Application, not a plugin
Main question:
- Where do I register a plugin for a dart isolate/headleess flutter engine in iOS native
Bonus questions:
- do I need to manually destroy the flutter engine? (see cleanUpFlutterResources)
- in regards to running while the app is terminated, is it best practice to create a flutter engine and destroy it every time a background iOS delegate callback is invoked, or should I just create one isolate with a background channel that is running all the time?
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
guard let callbackHandle = LocalStorage.getStoredCallbackHandle(),
let flutterCallbackInformation = FlutterCallbackCache.lookupCallbackInformation(callbackHandle) else {
return
}
var flutterEngine: FlutterEngine? = FlutterEngine(name: "atc_location_swift.BGThread", project: nil, allowHeadlessExecution: true)
flutterEngine!.run(withEntrypoint: flutterCallbackInformation.callbackName, libraryURI: flutterCallbackInformation.callbackLibraryPath)
flutterEngine!.registrar(forPlugin: "FLTPathProviderPlugin")
var backgroundMethodChannel: FlutterMethodChannel? = FlutterMethodChannel(name: "plugins.atc.io/location_background", binaryMessenger: flutterEngine!.binaryMessenger)
func cleanupFlutterResources() {
flutterEngine?.destroyContext()
backgroundMethodChannel = nil
flutterEngine = nil
}
backgroundMethodChannel?.setMethodCallHandler { (call, result) in
result(true)
backgroundMethodChannel?.invokeMethod("triggerBackgroundTask", arguments: ["lat": 0.1, "lon": 0.2], result: { flutterResult in
print("flutter result \(flutterResult ?? "no result sent")")
cleanupFlutterResources()
})
}
print("location: \(location.coordinate)")
}
}
Here is my dart isolate. Basically I am printing "callback called now -> $now" but when I try to addItemToDataList(), the path provider plugin gets referenced and throws a missing plugin exception
void callback() async {
WidgetsFlutterBinding.ensureInitialized();
const MethodChannel _backgroundChannel =
MethodChannel('plugins.atc.io/location_background');
_backgroundChannel.setMethodCallHandler((MethodCall call) async {
try {
WidgetsFlutterBinding.ensureInitialized();
final String now = DateTime.now().toString();
print("callback called now -> $now");
await LocationDataSource.addItemToDataList(now);
return Future.value(true);
} catch (e) {
print("exception calling callback: $e");
return Future.value(false);
}
});
_backgroundChannel.invokeMethod('backgroundChannelInitialized');
}
Here is my GeneratedPluginRegistrant, it seems like when calling flutter run, the dependency (path_provider) gets registered with the native code here, but that doesn't seem to do the trick when calling the path provider plugin from a dart isolate
#if __has_include(<path_provider/FLTPathProviderPlugin.h>)
#import <path_provider/FLTPathProviderPlugin.h>
#else
@import path_provider;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]];
}
@end