Hi All,
We are redeveloping the ios app using flutter where it receives the push notification from APNs server that is further processed by clicking actions Allow/Deny listed after tapping the push notification message in tray.
Problem - When Flutter-iOS app is closed/terminated state and user click Allow/Deny action on tapping the push notification message, then app gets openup up but not able to get hold of the action and related push notification payload. Following is AppDelegate.m and PushPlugin.m code that handles the push notification. Few observations
- When app is opening up didFinishLaunchingWithOptions method gets called but that does not contain any details about action and push notification payload.
- In non-flutter app, I noticed didFinishLaunchingWithOptions first gets called and after that didReceiveNotificationResponse gets called that has the action with payload details for processing.
Can you please help me to understand - Does flutter-ios app should call didReceiveNotificationResponse callback method to handle the push notification message?
- What are the changes we should make to handle action with push notification message
FYI, if I directly click on push notification instead of actions listed on the message then didFinishLaunchingWithOptions able to read the notification details.
AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.flutterEngineInitialized = NO; // Initially, engine is not ready
// Initialize the FlutterViewController to access the Flutter engine
self.flutterViewController = (FlutterViewController *)self.window.rootViewController;
[GeneratedPluginRegistrant registerWithRegistry:self];
// Register the PushPlugin
[PushPlugin registerWithRegistrar:[self registrarForPlugin:@"PushPlugin"]];
// Call PushPlugin to request notification permissions
[PushPlugin performTaskPostNotificationPermissionAllowed];
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
NSString *actionIdentifier = userInfo[@"actionIdentifier"];
if (userInfo) {
[self handleNotificationLaunchAfterFlutterInitialization:actionIdentifier userInfo:userInfo];
} else {
NSLog(@"Error: Notification data is missing.");
}
}
// Watch for the Flutter engine initialization
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.flutterEngineInitialized = YES; // Set the engine as initialized after a small delay
});
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)handleNotificationLaunchAfterFlutterInitialization:(NSString *)actionIdentifier userInfo:(NSDictionary *)userInfo {
if (self.flutterViewController) {
// Check if the Flutter engine is ready
if (self.flutterEngineInitialized) {
// Now that Flutter is ready, send the notification data to Flutter/dart code through method channel
[[PushPlugin sharedInstance] handleNotificationLaunch:actionIdentifier userInfo:userInfo];
} else {
// If Flutter engine isn't ready yet, defer the method call
NSLog(@"Flutter engine not ready, deferring notification handling.");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self handleNotificationLaunchAfterFlutterInitialization:actionIdentifier userInfo:userInfo];
});
}
}
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
NSDictionary *userInfo = response.notification.request.content.userInfo;
// Pass notification data to PushPlugin instance when the user clicks a notification
[[PushPlugin sharedInstance] userNotificationCenter:center
didReceiveNotificationResponse:response
withCompletionHandler:completionHandler];
}
================
PushPlugin.m
(void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel *channel = [FlutterMethodChannel
methodChannelWithName:OMACHANNEL
binaryMessenger:[registrar messenger]];
PushPlugin *instance = [self sharedInstance];
instance.channel = channel;
[registrar addMethodCallDelegate:instance channel:channel];
// Request permission for notifications
[PushPlugin performTaskPostNotificationPermissionAllowed ];
}
// Request notification permissions
- (void)requestNotificationPermissions {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self; // Set delegate for notification handling
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
});
}
}];
//This configures UNNotificationAction Allow/Deny and UNNotificationCategory
[PushPlugin registerForNotification];
}