Platform Method Channel From iOS Side to Flutter - ios

so i have an issue when trying to implement the platform method channel for my flutter project, When i try to invoke a method from flutter to ios side, it gets triggered and everything is working perfectly but when i try to invoke a method from ios side (appDelegate file) to flutter in order to perform a sepecific task, it's not working.
AppDelegate.swift Code:
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
application.registerForRemoteNotifications()
GeneratedPluginRegistrant.register(with: self)
let controller = (window?.rootViewController as! FlutterViewController)
let methodChannle = FlutterMethodChannel(name: "channelMethodTest", binaryMessenger: controller.binaryMessenger)
methodChannle.invokeMethod("taskName", arguments: [:])
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
Flutter main.dart:
const methodChannel = MethodChannel('channelMethodTest');
Future<void> methodHandler(MethodCall call) async {
final String idea = call.arguments;
switch (call.method) {
case "taskName":
print(
"receiving task from ios to flutter");
break;
default:
print('no method handler for method ${call.method}');
}
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
methodChannel.setMethodCallHandler(methodHandler);
}
I tried searching for tutos on how to do it but i cannot find any

you need to make the channelmethod var global then you need to instantiate the method channel in the didFinishLaunchingWithOptions func if you're using the method channel to invoke any method in the app deletage or any extension related to it.

Ensure that the channel name is the same in Flutter and iOS side. Now they seem to be different:
let methodChannle = FlutterMethodChannel(name: "channelMethodTest", binaryMessenger: controller.binaryMessenger)
const methodChannel = MethodChannel('beyang.com-channelMethodTest');

Related

Run a Swift UI Page from Flutter AppDelegate.swift file

I want to run a custom Swift UI page in my flutter application. I am trying to call ios platform specific code using method channel. My target is when i call a method using method channel i will present the Swift UI view in front of user.
My AppDelegate.swift file is below:
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "yourpackageName/channelName",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
if call.method == "showPage"{
//run a swift ui view here
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
And my custom Swift UI file is below:
import SwiftUI
#available(iOS 13.0, *)
struct CustomPage: View {
var body: some View {
Text("Hello, World!")
}
}
#available(iOS 13.0, *)
struct CustomPage_Previews: PreviewProvider {
static var previews: some View {
CustomPage()
}
}
Is there any way i can achieve this?

How to integrate Siri into Flutter with MethodChannels?

I want to integrate Sirikit into my flutter application. Therefore I have to establish a MethodChannel for communication between the host and the client. I want the iOS-side to invoke a flutter function and that response be Siris answer.
This is the flutter Side:
void nativeFunc() {
const platform = const MethodChannel("flutter.siri");
Future<dynamic> _handleMethod(MethodCall call) async {
if (call.method == "message") {
return api_request("Physics", call.arguments, 50);
}
}
platform.setMethodCallHandler(_handleMethod);
}
Then I have to specify the methodChannel on the iOS-side:
//AppDelegate.swift
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name:"flutter.siri")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
//IntentHandler.swift
func handle(intent: INSendMessageIntent, completion: #escaping (INSendMessageIntentResponse) -> Void) {
// Implement your application logic to send a message here.
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
let response = methodChannel.invokeMethod()//this should invoke the MethodChannel-function from Flutter
completion(response)
}
I am no Ios-developer so I have no clue how to actually do this. Right now the problem is that the IntentHandler.swift-File doesn't have access to the method channel data because it was declared in another file. How can my Intenthandler-file call the flutter method and respond with Siri?
I was facing a similar issue and I was able to make it work.
The problem is that Siri intents are in a different Target because they can run in the background if needed and because of this your communication using Method channels would be really complicated to implement (if possible).
My approach was to use the flutter extension HomeWidget
https://pub.dev/packages/home_widget
Since Home widgets also use a different target and This extension opens a communication channel using App groups and UserDefaults we could use the same communication channel to send data to our Siri Intent.
Just follow the configuration guide of the HomeWidget extension and add AppGroups capabilities to your runner Target and also to your Siri intent.
make sure you use the same group ID in all of them including on the flutter side:
HomeWidget.setAppGroupId('YOUR_GROUP_ID');
Then you can use the extension methods to send and receive data between flutter and swift.

Flutter - How to invoke channel in native Swift code?

I have written native Swift code in my AppDelegate, I am trying to invoke a third party sdk callback in my dart side of code, however the declaration of my method channel can only be done in application function. How do I invoke a channel in my delegate methods to make a dart callback?
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate, TJPlacementDelegate {
public func requestDidSucceed(_ placement: TJPlacement!) {
//Need to invoke methodChannel here
}
public func requestDidFail(_ placement: TJPlacement!, error: Error!) {
//Need to invoke methodChannel here
}
public func contentIsReady(_ placement: TJPlacement!) {
//Need to invoke methodChannel here
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let channelName = "ios_native"
let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: rootViewController as! FlutterBinaryMessenger)
methodChannel.setMethodCallHandler {(call: FlutterMethodCall, result: FlutterResult) -> Void in
switch(call.method) {
case "setDebugEnabled":
let isDebug = call.arguments as! Bool
Tapjoy.setDebugEnabled(isDebug)
break;
default: result(FlutterMethodNotImplemented)
}
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
I can't declare my methodChannel outside my application as the window doesn't get recognised.
Define the method channel in the AppDelegate:
var methodChannel: FlutterMethodChannel!
Initialize it:
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
...
methodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: rootViewController as! FlutterBinaryMessenger)
}
Now you can call invokeMethod:
public func requestDidSucceed(_ placement: TJPlacement!) {
methodChannel.invokeMethod()
}

How can plugins be used in another plugin's callback in the background?

I'm using this background_locator plugin and a geofencing plugin in my Flutter app. In the location callback function of the background_locator plugin I create geofences using the geofencing plugin. This works fine on Android. On iOS the background_locator plugin functions as it should, but calling geofencing methods in the callback has no effect. Calling the geofencing methods outside of the callback works without problems.
I found some resources addressing this: https://github.com/flutter/flutter/issues/21925 and https://github.com/flutter/engine/pull/7843 and changed the code in my AppDelegate.swift accordingly, but this issue still occurs. Any advice is appreciated!
Below is my AppDelegate.swift
import UIKit
import Flutter
import GoogleMaps
func registerPlugins(registry: FlutterPluginRegistry) -> () {
if (!registry.hasPlugin("BackgroundLocatorPlugin")) {
BackgroundLocatorPlugin.register(with: registry.registrar(forPlugin: "BackgroundLocatorPlugin"))
}
if (!registry.hasPlugin("GeofencingPlugin")) {
GeofencingPlugin.register(with: registry.registrar(forPlugin: "GeofencingPlugin"))
}
if (!registry.hasPlugin("FlutterLocalNotificationsPlugin")) {
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
}
if (!registry.hasPlugin("FLTSharedPreferencesPlugin")) {
FLTSharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "FLTSharedPreferencesPlugin"))
}
if (!registry.hasPlugin("FlutterSecureStoragePlugin")) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
}
}
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("$GOOGLE_MAPS_API_KEY")
GeneratedPluginRegistrant.register(with: self)
GeofencingPlugin.setPluginRegistrantCallback(registerPlugins)
BackgroundLocatorPlugin.setPluginRegistrantCallback(registerPlugins)
// iOS notification setup
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
// Prevent "old" notifications showing up after reinstallation
if(!UserDefaults.standard.bool(forKey: "Notification")) {
UIApplication.shared.cancelAllLocalNotifications()
UserDefaults.standard.set(true, forKey: "Notification")
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
I have face the issue with "FlutterGeofencing" that app will caught an exception "failed to set registerPlugins" and resolve the issue by set the callback
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
GeofencingPlugin.setPluginRegistrantCallback({_ in
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

Xcode giving error Value of type 'FlutterViewController' has no member 'binaryMessenger' when writing ios native swift code in flutter project

I made a flutter project with command
flutter create -i swift myProject
then I changed the directory to myProject with
cd myProject
and ther I run
pod setup.
It run swiftly but when I added code to AppDeligate.swift in ios/Runner/Runner directory according to the tutorial here:
https://flutter.dev/docs/development/platform-integration/platform-channels
Here is my AppDeligate.swift:
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
// Handle battery messages.
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
I am getting error "Value of type 'FlutterViewController' has no member 'binaryMessenger'"
Am I missing something?
Change this line
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
To ->
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller as! FlutterBinaryMessenger)
It was changed in latest flutter upgrade.
When using Flutter MACOS platform this will not help, you need to use this code:
let batteryChannel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: controller.engine.binaryMessenger
)

Resources