From 8e841b37c1efe829acaf68f5db6eecc8c557b08e Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Sun, 29 Dec 2019 12:09:40 +0100 Subject: [PATCH 01/10] added ios support --- .../ios/Classes/FLTFirebaseMessagingPlugin.m | 151 +++++++++++++++++- 1 file changed, 146 insertions(+), 5 deletions(-) diff --git a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m index c763f293c700..402e84000480 100644 --- a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m @@ -14,6 +14,11 @@ @interface FLTFirebaseMessagingPlugin () @end #endif +static NSString* backgroundSetupCallback = @"background_setup_callback"; +static NSString* backgroundMessageCallback = @"background_message_callback"; +static FlutterPluginRegistrantCallback registerPlugins = nil; +typedef void (^FetchCompletionHandler)(UIBackgroundFetchResult result); + static FlutterError *getFlutterError(NSError *error) { if (error == nil) return nil; return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", (long)error.code] @@ -25,17 +30,29 @@ @interface FLTFirebaseMessagingPlugin () @implementation FLTFirebaseMessagingPlugin { FlutterMethodChannel *_channel; + FlutterMethodChannel *_backgroundChannel; + NSUserDefaults *_userDefaults; + NSObject *_registrar; NSDictionary *_launchNotification; + NSMutableArray *_eventQueue; BOOL _resumingFromBackground; + FlutterEngine *_headlessRunner; + BOOL initialized; + FetchCompletionHandler fetchCompletionHandler; +} + ++ (void)setPluginRegistrantCallback:(FlutterPluginRegistrantCallback)callback { + registerPlugins = callback; } + (void)registerWithRegistrar:(NSObject *)registrar { + NSLog(@"registerWithRegistrar"); _registrar = registrar; FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/firebase_messaging" binaryMessenger:[registrar messenger]]; FLTFirebaseMessagingPlugin *instance = - [[FLTFirebaseMessagingPlugin alloc] initWithChannel:channel]; + [[FLTFirebaseMessagingPlugin alloc] initWithChannel:channel registrar:registrar]; [registrar addApplicationDelegate:instance]; [registrar addMethodCallDelegate:instance channel:channel]; @@ -45,7 +62,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { } } -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel registrar:(NSObject *)registrar { self = [super init]; if (self) { @@ -57,12 +74,20 @@ - (instancetype)initWithChannel:(FlutterMethodChannel *)channel { NSLog(@"Configured the default Firebase app %@.", [FIRApp defaultApp].name); } [FIRMessaging messaging].delegate = self; + + // Setup background handling + _userDefaults = [NSUserDefaults standardUserDefaults]; + _eventQueue = [[NSMutableArray alloc] init]; + _registrar = registrar; + _headlessRunner = [[FlutterEngine alloc] initWithName:@"firebase_messaging_background" project:nil allowHeadlessExecution:YES]; + _backgroundChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/firebase_messaging_background" binaryMessenger:[_headlessRunner binaryMessenger]]; } return self; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { NSString *method = call.method; + NSLog(@"handleMethodCall : %@", method); if ([@"requestNotificationPermissions" isEqualToString:method]) { NSDictionary *arguments = call.arguments; if (@available(iOS 10.0, *)) { @@ -136,6 +161,30 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [[UIApplication sharedApplication] registerForRemoteNotifications]; result([NSNumber numberWithBool:YES]); } + } else if ([@"FcmDartService#start" isEqualToString:method]) { + NSDictionary *arguments = call.arguments; + NSLog(@"FcmDartService#start"); + long setupHandle = [arguments[@"setupHandle"] longValue]; + long backgroundHandle = [arguments[@"backgroundHandle"] longValue]; + NSLog(@"FcmDartService#start with handle : %ld", setupHandle); + [self saveCallbackHandle:backgroundSetupCallback handle:setupHandle]; + [self saveCallbackHandle:backgroundMessageCallback handle:backgroundHandle]; + result(nil); + } else if ([@"FcmDartService#initialized" isEqualToString:method]) { + /** + * Acknowledge that background message handling on the Dart side is ready. This is called by the + * Dart side once all background initialization is complete via `FcmDartService#initialized`. + */ + @synchronized(self) { + initialized = YES; + while ([_eventQueue count] > 0) { + NSArray* call = _eventQueue[0]; + [_eventQueue removeObjectAtIndex:0]; + + [self invokeMethod:call[0] callbackHandle:[call[1] longLongValue] arguments:call[2]]; + } + } + result(nil); } else if ([@"configure" isEqualToString:method]) { [FIRMessaging messaging].shouldEstablishDirectChannel = true; [[UIApplication sharedApplication] registerForRemoteNotifications]; @@ -196,6 +245,7 @@ - (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMess #endif - (void)didReceiveRemoteNotification:(NSDictionary *)userInfo { + NSLog(@"didReceiveRemoteNotification"); if (_resumingFromBackground) { [_channel invokeMethod:@"onResume" arguments:userInfo]; } else { @@ -238,9 +288,23 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { - (BOOL)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { - [self didReceiveRemoteNotification:userInfo]; - completionHandler(UIBackgroundFetchResultNoData); + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { + NSLog(@"didReceiveRemoteNotification:completionHandler"); + if (application.applicationState == UIApplicationStateBackground) { + //save this handler for later so it can be completed + fetchCompletionHandler = completionHandler; + + [self queueMethodCall:@"handleBackgroundMessage" callbackName:backgroundMessageCallback arguments:userInfo]; + + if (!initialized){ + [self startBackgroundRunner]; + } + + } else { + [self didReceiveRemoteNotification:userInfo]; + completionHandler(UIBackgroundFetchResultNewData); + } + return YES; } @@ -278,4 +342,81 @@ - (void)messaging:(FIRMessaging *)messaging [_channel invokeMethod:@"onMessage" arguments:remoteMessage.appData]; } +- (void)setupBackgroundHandling:(int64_t)handle { + NSLog(@"Setting up Firebase background handling"); + + [self saveCallbackHandle:backgroundSetupCallback handle:handle]; + + NSLog(@"Finished background setup"); +} + +- (void)startBackgroundRunner { + NSLog(@"Starting background runner"); + + int64_t handle = [self getCallbackHandle:backgroundSetupCallback]; + + FlutterCallbackInformation *info = [FlutterCallbackCache lookupCallbackInformation:handle]; + NSAssert(info != nil, @"failed to find callback"); + NSString *entrypoint = info.callbackName; + NSString *uri = info.callbackLibraryPath; + + [_headlessRunner runWithEntrypoint:entrypoint libraryURI:uri]; + [_registrar addMethodCallDelegate:self channel:_backgroundChannel]; + + // Once our headless runner has been started, we need to register the application's plugins + // with the runner in order for them to work on the background isolate. `registerPlugins` is + // a callback set from AppDelegate.m in the main application. This callback should register + // all relevant plugins (excluding those which require UI). + + NSAssert(registerPlugins != nil, @"failed to set registerPlugins"); + registerPlugins(_headlessRunner); +} + +- (int64_t)getCallbackHandle:(NSString *) key { + NSLog(@"Getting callback handle for key %@", key); + id handle = [_userDefaults objectForKey:key]; + if (handle == nil) { + return 0; + } + return [handle longLongValue]; +} + +- (void) saveCallbackHandle:(NSString *)key handle:(int64_t)handle { + NSLog(@"Saving callback handle for key %@", key); + + [_userDefaults setObject:[NSNumber numberWithLongLong:handle] forKey:key]; +} + +- (void) queueMethodCall:(NSString *) method callbackName:(NSString*)callback arguments:(NSDictionary*)arguments { + NSLog(@"Queuing method call: %@", method); + int64_t handle = [self getCallbackHandle:callback]; + + @synchronized(self) { + if (initialized) { + [self invokeMethod:method callbackHandle:handle arguments:arguments]; + } else { + NSArray *call = @[method, @(handle), arguments]; + [_eventQueue addObject:call]; + } + } +} + +- (void) invokeMethod:(NSString *) method callbackHandle:(long)handle arguments:(NSDictionary*)arguments { + NSLog(@"Invoking method: %@", method); + //NSArray* args = @[@(handle), arguments]; + + NSDictionary *aaa = @{ + @"handle" : @(handle), + @"message" : arguments, + }; + + [_backgroundChannel invokeMethod:method arguments:aaa result:^(id _Nullable result) { + NSLog(@"%@ method completed", method); + if (self->fetchCompletionHandler!=nil) { + self->fetchCompletionHandler(UIBackgroundFetchResultNewData); + self->fetchCompletionHandler = nil; + } + }]; +} + @end From 4d0844a69be915008bbffe697c589aee432ee56a Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Wed, 8 Jan 2020 10:25:55 +0100 Subject: [PATCH 02/10] - renamed variable and removed commented log --- .../ios/Classes/FLTFirebaseMessagingPlugin.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m index 402e84000480..898f3d2db77b 100644 --- a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m @@ -403,14 +403,13 @@ - (void) queueMethodCall:(NSString *) method callbackName:(NSString*)callback ar - (void) invokeMethod:(NSString *) method callbackHandle:(long)handle arguments:(NSDictionary*)arguments { NSLog(@"Invoking method: %@", method); - //NSArray* args = @[@(handle), arguments]; - NSDictionary *aaa = @{ + NSDictionary *callbackArguments = @{ @"handle" : @(handle), @"message" : arguments, }; - [_backgroundChannel invokeMethod:method arguments:aaa result:^(id _Nullable result) { + [_backgroundChannel invokeMethod:method arguments:callbackArguments result:^(id _Nullable result) { NSLog(@"%@ method completed", method); if (self->fetchCompletionHandler!=nil) { self->fetchCompletionHandler(UIBackgroundFetchResultNewData); From 07fc453be319b230cd05dec057c9a380caa0d81b Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Tue, 18 Feb 2020 15:26:12 +0100 Subject: [PATCH 03/10] [firebase_messaging] Added instructions for ios background handling in readme (and some restructuring in readme) --- packages/firebase_messaging/README.md | 72 ++++++++++++++++----------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index 5bf421d03177..1857fb224ef5 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -52,8 +52,39 @@ Note: When you are debugging on Android, use a device or AVD with Google Play se ``` -#### Optionally handle background messages +### iOS Integration + +To integrate your plugin into the iOS part of your app, follow these steps: + +1. Generate the certificates required by Apple for receiving push notifications following [this guide](https://firebase.google.com/docs/cloud-messaging/ios/certs) in the Firebase docs. You can skip the section titled "Create the Provisioning Profile". + +1. Using the [Firebase Console](https://console.firebase.google.com/) add an iOS app to your project: Follow the assistant, download the generated `GoogleService-Info.plist` file, open `ios/Runner.xcworkspace` with Xcode, and within Xcode place the file inside `ios/Runner`. **Don't** follow the steps named "Add Firebase SDK" and "Add initialization code" in the Firebase assistant. + +1. In Xcode, select `Runner` in the Project Navigator. In the Capabilities Tab turn on `Push Notifications` and `Background Modes`, and enable `Background fetch` and `Remote notifications` under `Background Modes`. + +1. Follow the steps in the "[Upload your APNs certificate](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_certificate)" section of the Firebase docs. + +1. Add the following lines to the `(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` +method in the `AppDelegate.m`/`AppDelegate.swift` of your iOS project. + +Objective-C: +```objectivec +if (@available(iOS 10.0, *)) { + [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; +} +``` + +Swift: +```swift +if #available(iOS 10.0, *) { + UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate +} +``` + +### Handle background messages (Optional) + +#### Android configuration >Background message handling is intended to be performed quickly. Do not perform long running tasks as they may not be allowed to finish by the Android system. See [Background Execution Limits](https://developer.android.com/about/versions/oreo/background) @@ -110,6 +141,16 @@ By default background messaging is not enabled. To handle messages in the backgr ``` + +#### iOS configuration (Swift) +1. In `AppDelegate.swift`, add the following: +```swift + FLTFirebaseMessagingPlugin.setPluginRegistrantCallback({ (registry: FlutterPluginRegistry) -> Void in + GeneratedPluginRegistrant.register(with: registry); + }); +``` + +#### Usage in the common Dart code 1. Define a **TOP-LEVEL** or **STATIC** function to handle background messages ```dart @@ -155,35 +196,6 @@ By default background messaging is not enabled. To handle messages in the backgr so that it can be ready to receive messages as early as possible. See the [example app](https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_messaging/example) for a demonstration. -### iOS Integration - -To integrate your plugin into the iOS part of your app, follow these steps: - -1. Generate the certificates required by Apple for receiving push notifications following [this guide](https://firebase.google.com/docs/cloud-messaging/ios/certs) in the Firebase docs. You can skip the section titled "Create the Provisioning Profile". - -1. Using the [Firebase Console](https://console.firebase.google.com/) add an iOS app to your project: Follow the assistant, download the generated `GoogleService-Info.plist` file, open `ios/Runner.xcworkspace` with Xcode, and within Xcode place the file inside `ios/Runner`. **Don't** follow the steps named "Add Firebase SDK" and "Add initialization code" in the Firebase assistant. - -1. In Xcode, select `Runner` in the Project Navigator. In the Capabilities Tab turn on `Push Notifications` and `Background Modes`, and enable `Background fetch` and `Remote notifications` under `Background Modes`. - -1. Follow the steps in the "[Upload your APNs certificate](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_certificate)" section of the Firebase docs. - -1. Add the following lines to the `(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` -method in the `AppDelegate.m`/`AppDelegate.swift` of your iOS project. - -Objective-C: -```objectivec -if (@available(iOS 10.0, *)) { - [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; -} -``` - -Swift: -```swift -if #available(iOS 10.0, *) { - UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate -} -``` - ### Dart/Flutter Integration From your Dart code, you need to import the plugin and instantiate it: From 1824a2a8fc106958fac811ffc430401efa6e3cdc Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Thu, 27 Feb 2020 13:38:46 +0100 Subject: [PATCH 04/10] [firebase_messaging] Fixed some formatting --- .../ios/Classes/FLTFirebaseMessagingPlugin.m | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m index 898f3d2db77b..3d06ff03094d 100644 --- a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m @@ -381,13 +381,16 @@ - (int64_t)getCallbackHandle:(NSString *) key { return [handle longLongValue]; } -- (void) saveCallbackHandle:(NSString *)key handle:(int64_t)handle { +- (void)saveCallbackHandle:(NSString *)key + handle:(int64_t)handle { NSLog(@"Saving callback handle for key %@", key); [_userDefaults setObject:[NSNumber numberWithLongLong:handle] forKey:key]; } -- (void) queueMethodCall:(NSString *) method callbackName:(NSString*)callback arguments:(NSDictionary*)arguments { +- (void)queueMethodCall:(NSString *)method + callbackName:(NSString*)callback + arguments:(NSDictionary*)arguments { NSLog(@"Queuing method call: %@", method); int64_t handle = [self getCallbackHandle:callback]; @@ -395,13 +398,15 @@ - (void) queueMethodCall:(NSString *) method callbackName:(NSString*)callback ar if (initialized) { [self invokeMethod:method callbackHandle:handle arguments:arguments]; } else { - NSArray *call = @[method, @(handle), arguments]; + NSArray *call = @[ method, @(handle), arguments ]; [_eventQueue addObject:call]; } } } -- (void) invokeMethod:(NSString *) method callbackHandle:(long)handle arguments:(NSDictionary*)arguments { +- (void)invokeMethod:(NSString *)method + callbackHandle:(long)handle + arguments:(NSDictionary*)arguments { NSLog(@"Invoking method: %@", method); NSDictionary *callbackArguments = @{ @@ -409,13 +414,15 @@ - (void) invokeMethod:(NSString *) method callbackHandle:(long)handle arguments: @"message" : arguments, }; - [_backgroundChannel invokeMethod:method arguments:callbackArguments result:^(id _Nullable result) { - NSLog(@"%@ method completed", method); - if (self->fetchCompletionHandler!=nil) { - self->fetchCompletionHandler(UIBackgroundFetchResultNewData); - self->fetchCompletionHandler = nil; - } - }]; + [_backgroundChannel invokeMethod:method + arguments:callbackArguments + result:^(id _Nullable result) { + NSLog(@"%@ method completed", method); + if (self->fetchCompletionHandler!=nil) { + self->fetchCompletionHandler(UIBackgroundFetchResultNewData); + self->fetchCompletionHandler = nil; + } + }]; } @end From 02257066c1b9473bbcf1b5d148bfc9730f17e0d0 Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Thu, 27 Feb 2020 21:08:31 +0100 Subject: [PATCH 05/10] [firebase_messaging] Formatted the objective-c code --- .../ios/Classes/FLTFirebaseMessagingPlugin.m | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m index 3d06ff03094d..869b62f0167c 100644 --- a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m @@ -14,8 +14,8 @@ @interface FLTFirebaseMessagingPlugin () @end #endif -static NSString* backgroundSetupCallback = @"background_setup_callback"; -static NSString* backgroundMessageCallback = @"background_message_callback"; +static NSString *backgroundSetupCallback = @"background_setup_callback"; +static NSString *backgroundMessageCallback = @"background_message_callback"; static FlutterPluginRegistrantCallback registerPlugins = nil; typedef void (^FetchCompletionHandler)(UIBackgroundFetchResult result); @@ -42,7 +42,7 @@ @implementation FLTFirebaseMessagingPlugin { } + (void)setPluginRegistrantCallback:(FlutterPluginRegistrantCallback)callback { - registerPlugins = callback; + registerPlugins = callback; } + (void)registerWithRegistrar:(NSObject *)registrar { @@ -62,7 +62,8 @@ + (void)registerWithRegistrar:(NSObject *)registrar { } } -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel registrar:(NSObject *)registrar { +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel + registrar:(NSObject *)registrar { self = [super init]; if (self) { @@ -79,8 +80,12 @@ - (instancetype)initWithChannel:(FlutterMethodChannel *)channel registrar:(NSObj _userDefaults = [NSUserDefaults standardUserDefaults]; _eventQueue = [[NSMutableArray alloc] init]; _registrar = registrar; - _headlessRunner = [[FlutterEngine alloc] initWithName:@"firebase_messaging_background" project:nil allowHeadlessExecution:YES]; - _backgroundChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/firebase_messaging_background" binaryMessenger:[_headlessRunner binaryMessenger]]; + _headlessRunner = [[FlutterEngine alloc] initWithName:@"firebase_messaging_background" + project:nil + allowHeadlessExecution:YES]; + _backgroundChannel = [FlutterMethodChannel + methodChannelWithName:@"plugins.flutter.io/firebase_messaging_background" + binaryMessenger:[_headlessRunner binaryMessenger]]; } return self; } @@ -178,7 +183,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result @synchronized(self) { initialized = YES; while ([_eventQueue count] > 0) { - NSArray* call = _eventQueue[0]; + NSArray *call = _eventQueue[0]; [_eventQueue removeObjectAtIndex:0]; [self invokeMethod:call[0] callbackHandle:[call[1] longLongValue] arguments:call[2]]; @@ -288,15 +293,17 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { - (BOOL)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { NSLog(@"didReceiveRemoteNotification:completionHandler"); if (application.applicationState == UIApplicationStateBackground) { - //save this handler for later so it can be completed + // save this handler for later so it can be completed fetchCompletionHandler = completionHandler; - [self queueMethodCall:@"handleBackgroundMessage" callbackName:backgroundMessageCallback arguments:userInfo]; + [self queueMethodCall:@"handleBackgroundMessage" + callbackName:backgroundMessageCallback + arguments:userInfo]; - if (!initialized){ + if (!initialized) { [self startBackgroundRunner]; } @@ -372,25 +379,24 @@ - (void)startBackgroundRunner { registerPlugins(_headlessRunner); } -- (int64_t)getCallbackHandle:(NSString *) key { +- (int64_t)getCallbackHandle:(NSString *)key { NSLog(@"Getting callback handle for key %@", key); id handle = [_userDefaults objectForKey:key]; if (handle == nil) { - return 0; + return 0; } return [handle longLongValue]; } -- (void)saveCallbackHandle:(NSString *)key - handle:(int64_t)handle { +- (void)saveCallbackHandle:(NSString *)key handle:(int64_t)handle { NSLog(@"Saving callback handle for key %@", key); [_userDefaults setObject:[NSNumber numberWithLongLong:handle] forKey:key]; } - (void)queueMethodCall:(NSString *)method - callbackName:(NSString*)callback - arguments:(NSDictionary*)arguments { + callbackName:(NSString *)callback + arguments:(NSDictionary *)arguments { NSLog(@"Queuing method call: %@", method); int64_t handle = [self getCallbackHandle:callback]; @@ -406,9 +412,9 @@ - (void)queueMethodCall:(NSString *)method - (void)invokeMethod:(NSString *)method callbackHandle:(long)handle - arguments:(NSDictionary*)arguments { + arguments:(NSDictionary *)arguments { NSLog(@"Invoking method: %@", method); - + NSDictionary *callbackArguments = @{ @"handle" : @(handle), @"message" : arguments, @@ -416,9 +422,9 @@ - (void)invokeMethod:(NSString *)method [_backgroundChannel invokeMethod:method arguments:callbackArguments - result:^(id _Nullable result) { + result:^(id _Nullable result) { NSLog(@"%@ method completed", method); - if (self->fetchCompletionHandler!=nil) { + if (self->fetchCompletionHandler != nil) { self->fetchCompletionHandler(UIBackgroundFetchResultNewData); self->fetchCompletionHandler = nil; } From 34ad709d1c7e22ae8a9ad69786b253b299d62477 Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Fri, 27 Mar 2020 09:42:08 +0100 Subject: [PATCH 06/10] [firebase_messaging] Fixed readme after merge --- packages/firebase_messaging/README.md | 96 +++++++++++++++------------ 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index 538436b8c86a..ccde3965e127 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -52,8 +52,46 @@ Note: When you are debugging on Android, use a device or AVD with Google Play se ``` -#### Optionally handle background messages +### iOS Integration + +To integrate your plugin into the iOS part of your app, follow these steps: + +1. Generate the certificates required by Apple for receiving push notifications following [this guide](https://firebase.google.com/docs/cloud-messaging/ios/certs) in the Firebase docs. You can skip the section titled "Create the Provisioning Profile". + +1. Using the [Firebase Console](https://console.firebase.google.com/) add an iOS app to your project: Follow the assistant, download the generated `GoogleService-Info.plist` file, open `ios/Runner.xcworkspace` with Xcode, and within Xcode place the file inside `ios/Runner`. **Don't** follow the steps named "Add Firebase SDK" and "Add initialization code" in the Firebase assistant. + +1. In Xcode, select `Runner` in the Project Navigator. In the Capabilities Tab turn on `Push Notifications` and `Background Modes`, and enable `Background fetch` and `Remote notifications` under `Background Modes`. + +1. Follow the steps in the "[Upload your APNs certificate](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_certificate)" section of the Firebase docs. + +1. If you need to disable the method swizzling done by the FCM iOS SDK (e.g. so that you can use this plugin with other notification plugins) then add the following to your application's `Info.plist` file. + +```xml +FirebaseAppDelegateProxyEnabled + +``` + +After that, add the following lines to the `(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` +method in the `AppDelegate.m`/`AppDelegate.swift` of your iOS project. + +Objective-C: +```objectivec +if (@available(iOS 10.0, *)) { + [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; +} +``` + +Swift: +```swift +if #available(iOS 10.0, *) { + UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate +} +``` + +### Handle background messages (Optional) + +#### Android configuration >Background message handling is intended to be performed quickly. Do not perform long running tasks as they may not be allowed to finish by the Android system. See [Background Execution Limits](https://developer.android.com/about/versions/oreo/background) @@ -110,6 +148,15 @@ By default background messaging is not enabled. To handle messages in the backgr ``` +#### iOS configuration (Swift) +1. In `AppDelegate.swift`, add the following: +```swift + FLTFirebaseMessagingPlugin.setPluginRegistrantCallback({ (registry: FlutterPluginRegistry) -> Void in + GeneratedPluginRegistrant.register(with: registry); + }); +``` + +#### Usage in the common Dart code 1. Define a **TOP-LEVEL** or **STATIC** function to handle background messages ```dart @@ -155,41 +202,6 @@ By default background messaging is not enabled. To handle messages in the backgr so that it can be ready to receive messages as early as possible. See the [example app](https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_messaging/example) for a demonstration. -### iOS Integration - -To integrate your plugin into the iOS part of your app, follow these steps: - -1. Generate the certificates required by Apple for receiving push notifications following [this guide](https://firebase.google.com/docs/cloud-messaging/ios/certs) in the Firebase docs. You can skip the section titled "Create the Provisioning Profile". - -1. Using the [Firebase Console](https://console.firebase.google.com/) add an iOS app to your project: Follow the assistant, download the generated `GoogleService-Info.plist` file, open `ios/Runner.xcworkspace` with Xcode, and within Xcode place the file inside `ios/Runner`. **Don't** follow the steps named "Add Firebase SDK" and "Add initialization code" in the Firebase assistant. - -1. In Xcode, select `Runner` in the Project Navigator. In the Capabilities Tab turn on `Push Notifications` and `Background Modes`, and enable `Background fetch` and `Remote notifications` under `Background Modes`. - -1. Follow the steps in the "[Upload your APNs certificate](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_certificate)" section of the Firebase docs. - -1. If you need to disable the method swizzling done by the FCM iOS SDK (e.g. so that you can use this plugin with other notification plugins) then add the following to your application's `Info.plist` file. - -```xml -FirebaseAppDelegateProxyEnabled - -``` - -After that, add the following lines to the `(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` -method in the `AppDelegate.m`/`AppDelegate.swift` of your iOS project. - -Objective-C: -```objectivec -if (@available(iOS 10.0, *)) { - [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; -} -``` - -Swift: -```swift -if #available(iOS 10.0, *) { - UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate -} -``` ### Dart/Flutter Integration @@ -207,12 +219,12 @@ Next, you should probably request permissions for receiving Push Notifications. Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, and `onResume` callbacks that you configured with the plugin during setup. Here is how different message types are delivered on the supported platforms: -| | App in Foreground | App in Background | App Terminated | -| --------------------------: | ----------------- | ----------------- | -------------- | -| **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | -| **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires. | -| **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | -| **Data Message on iOS** | `onMessage` | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | +| | App in Foreground | App in Background | App Terminated | +| --------------------------: | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | +| **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires. | +| **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | +| **Data Message on iOS** | `onMessage` | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | Additional reading: Firebase's [About FCM Messages](https://firebase.google.com/docs/cloud-messaging/concept-options). From afcb41b3f0708be9eebe8bc405a0ed5cf63b14ad Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Fri, 27 Mar 2020 09:45:58 +0100 Subject: [PATCH 07/10] [firebase_messaging] updated versions --- packages/firebase_messaging/CHANGELOG.md | 4 ++++ packages/firebase_messaging/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/firebase_messaging/CHANGELOG.md b/packages/firebase_messaging/CHANGELOG.md index 26e6b0acc5f6..6fdd85932b6e 100644 --- a/packages/firebase_messaging/CHANGELOG.md +++ b/packages/firebase_messaging/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.15 + +* Added iOS support for background message handling. + ## 6.0.14 * Fix for missing UserAgent.h compilation failures. diff --git a/packages/firebase_messaging/pubspec.yaml b/packages/firebase_messaging/pubspec.yaml index 5d13c69a5162..23bc7484b83d 100644 --- a/packages/firebase_messaging/pubspec.yaml +++ b/packages/firebase_messaging/pubspec.yaml @@ -2,7 +2,7 @@ name: firebase_messaging description: Flutter plugin for Firebase Cloud Messaging, a cross-platform messaging solution that lets you reliably deliver messages on Android and iOS. homepage: https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_messaging -version: 6.0.14 +version: 6.0.15 flutter: plugin: From 566a9770e0379ec03d8744981f75bd98e35d1cc2 Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Wed, 17 Jun 2020 09:10:41 +0200 Subject: [PATCH 08/10] [firebase_messaging] Added import firebase_messaging to the readme. --- packages/firebase_messaging/README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index ccde3965e127..a48d7a26f36d 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -149,12 +149,19 @@ By default background messaging is not enabled. To handle messages in the backgr ``` #### iOS configuration (Swift) -1. In `AppDelegate.swift`, add the following: -```swift - FLTFirebaseMessagingPlugin.setPluginRegistrantCallback({ (registry: FlutterPluginRegistry) -> Void in - GeneratedPluginRegistrant.register(with: registry); - }); -``` +1. In the top of `AppDelegate.swift`, add the import of firebase_messaging: + + ```swift + import firebase_messaging + ``` + +1. Then add the following code to `AppDelegate.swift`: + + ```swift + FLTFirebaseMessagingPlugin.setPluginRegistrantCallback({ (registry: FlutterPluginRegistry) -> Void in + GeneratedPluginRegistrant.register(with: registry); + }); + ``` #### Usage in the common Dart code 1. Define a **TOP-LEVEL** or **STATIC** function to handle background messages From 9d495bf95b2c4ced30f4ac5ce0af94e3fe420ec3 Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Tue, 18 Aug 2020 15:02:46 +0200 Subject: [PATCH 09/10] [firebase_messaging] Updated readme with suggestions from @oskara --- packages/firebase_messaging/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index 487be9b52ac6..ca52559f2714 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -155,7 +155,7 @@ By default background messaging is not enabled. To handle messages in the backgr import firebase_messaging ``` -1. Then add the following code to `AppDelegate.swift`: +1. Then add the following code to `AppDelegate.swift` (E.g. last in the function `application` right before the return statement)): ```swift FLTFirebaseMessagingPlugin.setPluginRegistrantCallback({ (registry: FlutterPluginRegistry) -> Void in @@ -224,14 +224,14 @@ Next, you should probably request permissions for receiving Push Notifications. ## Receiving Messages -Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, and `onResume` callbacks that you configured with the plugin during setup. Here is how different message types are delivered on the supported platforms: +Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, `onResume` and `onBackgroundMessage` callbacks that you configured with the plugin during setup. Here is how different message types are delivered on the supported platforms: | | App in Foreground | App in Background | App Terminated | | --------------------------: | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | | **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires. | | **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | -| **Data Message on iOS** | `onMessage` | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | +| **Data Message on iOS** | `onMessage` | Message is delivered to `onBackgroundMessage`. This is the case both when app is running in background and the system has suspended the app. | Whan app is force-quit by the user the message is not handled. | Additional reading: Firebase's [About FCM Messages](https://firebase.google.com/docs/cloud-messaging/concept-options). From 5b71c9a7b2741a8d59d1d4d4b9515a9d5e5eeeac Mon Sep 17 00:00:00 2001 From: Tobias Junsten Date: Tue, 1 Sep 2020 07:55:09 +0200 Subject: [PATCH 10/10] - fixed spelling mistake in readme --- packages/firebase_messaging/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index ca52559f2714..228be891e4a6 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -231,7 +231,7 @@ Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, `onResume | **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | | **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires. | | **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | -| **Data Message on iOS** | `onMessage` | Message is delivered to `onBackgroundMessage`. This is the case both when app is running in background and the system has suspended the app. | Whan app is force-quit by the user the message is not handled. | +| **Data Message on iOS** | `onMessage` | Message is delivered to `onBackgroundMessage`. This is the case both when app is running in background and the system has suspended the app. | When app is force-quit by the user the message is not handled. | Additional reading: Firebase's [About FCM Messages](https://firebase.google.com/docs/cloud-messaging/concept-options).