firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.46k stars 3.91k forks source link

[firebase_messaging] Handle background messages on iOS #2016

Closed tobiasjunsten closed 3 years ago

tobiasjunsten commented 4 years ago

Description

Enable backgound handling on iOS to work the same way as android.

Related Issues

See Issue 47

Related pull request

This pull request was inspired by this one: Pull request 53

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

Finn0811 commented 4 years ago

Really looking forward to this, thanks for your work!

navidonline commented 4 years ago

Thanks for your great work, only worked in background .when app killed not work?

josh-burton commented 4 years ago

@shell32 what device are you testing on? If you force kill an app on iOS (common when testing things like this) then iOS will not deliver push notifications to that app until the app or device is restarted.

navidonline commented 4 years ago

@shell32 what device are you testing on? If you force kill an app on iOS (common when testing things like this) then iOS will not deliver push notifications to that app until the app or device is restarted.

iphone 6 , ios 12 ok thanks

tobiasjunsten commented 4 years ago

@shell32, just as @athornz says the app is not receiving any push notifications if the app has been force-quit. If the app has been suspended by the system (which happens quite fast) the app should be woken up and get some time to process a notification. When I've been testing I've also noticed that it can take a couple of minutes sometimes before the app is woken up if it has been suspended. You can read a bit more here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application#discussion

gustavoergalves commented 4 years ago

Looking forward to this, thanks for your work!

p30arena commented 4 years ago

can this pr solve this issue? https://github.com/FirebaseExtended/flutterfire/issues/357

tobiasjunsten commented 4 years ago

@p30arena No, it only deals with handling push messages when app is in background or suspended state for iOS.

campioncino commented 4 years ago

Can I finally see this behaviour in iOS? 7bd39177-e115-4f8f-8aac-a035bcc808f1

tobiasjunsten commented 4 years ago

@campioncino this pull request is not adding that behaviour. It's just about adding background handling for push messages. So when your app is in the background you'll be able to provide a dart function/isolate that should handle the message.

campioncino commented 4 years ago

@campioncino this pull request is not adding that behaviour. It's just about adding background handling for push messages. So when your app is in the background you'll be able to provide a dart function/isolate that should handle the message.

I'm sorry, I mean : can I finally do something like the image.

Using onBackgroundMessage: backgroundMessageHandler can I do something when my app is running in background?

tobiasjunsten commented 4 years ago

@campioncino Ok :) I guess that should be possible. Anything should be possible in the backgroundMessageHandler. Like fetching data from backend or trigger local notifications.

campioncino commented 4 years ago

@campioncino Ok :) I guess that should be possible. Anything should be possible in the backgroundMessageHandler. Like fetching data from backend or trigger local notifications.

I hope so, but i can't handle it. I can't do anythings, either a simple print. Are you able to do that? Maybe I am doing something wrong.

tobiasjunsten commented 4 years ago

@campioncino I'm currently fetching data from backend and scheduling local notifications based on the data fetched from backend and that works fine.

There are some debug prints in the ios code. Could you please try to connect your phone to the computer and open the console app on the computer. Then select your phone and filter the console output by "didReceiveRemoteNotification". Then try to send a push notification when your app is open and see if you get any logs. If you get the logs, try to just hit the home button to get the app in the background and send a new push notification and see if you still get a log entry.

campioncino commented 4 years ago

IT WORKS!!!!

misaelriojasftf commented 4 years ago

how can I test this?

tobiasjunsten commented 4 years ago

@misaelriojasftf You can test this pullrequest by adding this to your pubspec.yaml:

firebase_messaging:
    git:
      url: git://github.com/abilia/flutterfire.git
      ref: ios-background-support
      path: packages/firebase_messaging/
d3xt3r2909 commented 4 years ago

Firebase guys, can we speed up for a little bit this PR.

This is quite an important and crucial feature for every app, cannot understand why it is not yet ready for use. Why do I need a firebase messaging if they will not work in the background!?

misaelriojasftf commented 4 years ago

Hello @kalismeras61 , not unfortunately I couldn't. I just continue with other features. Push notifications are working so that's enough

timosturm commented 4 years ago

Does the background handler work for data messages also?

I try to show custom notifications and save incoming data from data-notifications, but the messages are still only received when the app is open or queried and received when the app is opened. I.e. only the callback for onMessage gets notifications.

I would appreciate any help.

@tobiasjunsten

kalismeras61 commented 4 years ago

Does the background handler work for data messages also?

I try to show custom notifications and save incoming data from data-notifications, but the messages are still only received when the app is open or queried and received when the app is opened. I.e. only the callback for onMessage gets notifications.

I would appreciate any help.

@tobiasjunsten

Yes it is working.

jengener commented 4 years ago

@misaelriojasftf You can test this pullrequest by adding this to your pubspec.yaml:

firebase_messaging:
    git:
      url: git://github.com/abilia/flutterfire.git
      ref: ios-background-support
      path: packages/firebase_messaging/

How to use this PR.

I found this problem when using


↳
    ** BUILD FAILED **
Xcode's output:
↳
    /Users/myaccount/.pub-cache/git/flutterfire-afcb41b3f0708be9eebe8bc405a0ed5cf63b14ad/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m:249:43: warning: 'FIRMessagingRemoteMessage' is deprecated: FCM direct channel is deprecated, please use APNs for downstream message handling. [-Wdeprecated-declarations]
    - (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
                                              ^
    In module 'FirebaseMessaging' imported from /Users/myaccount/my_app/ios/Pods/Headers/Public/Firebase/Firebase.h:72:
    /Users/myaccount/my_app/ios/Pods/FirebaseMessaging/Firebase/Messaging/Public/FIRMessaging.h:219:1: note: 'FIRMessagingRemoteMessage' has been explicitly marked deprecated here
    __deprecated_msg(
    ^
    In module 'UIKit' imported from /Users/myaccount/my_app/ios/Pods/Target Support Files/firebase_messaging/firebase_messaging-prefix.pch:2:
    In module 'Foundation' imported from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.5.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:8:
    In module 'CoreFoundation' imported from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.5.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:6:
    In module 'Darwin' imported from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.5.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h:16:
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.5.sdk/usr/include/sys/cdefs.h:191:48: note: expanded from macro '__deprecated_msg'
            #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                          ^```
tobiasjunsten commented 3 years ago

@jengchtk I will look into that.

isaiahtaylorhh commented 3 years ago

Thanks so much for your work on this. I was able to get it working as instructed. I did not get the deprecation error mentioned above. Let's get this merged!

isaiahtaylorhh commented 3 years ago

Hi, could you advise how it would be possible to use plugins from within the static handler method? I get I/flutter ( 9944): MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences) when I try to use SharedPreferences from within that function.

Edit: this seems to be related to https://github.com/flutter/flutter/issues/13937.

michaelgobbers commented 3 years ago

This sounds great however I've not been able to get this to work... I've modified my pubspec as the comment above and made the changes needed to my appdelegate.swift.

This is the message I'm sending to fcm

curl -X POST --header "Authorization: key=<MY_KEY>" \
    --Header "Content-Type: application/json" \
    https://fcm.googleapis.com/fcm/send \
    -d "{\"to\":\"fQ08m1akPULFqLDlNl4u_o:APA91bGjzqSuLdMlkPs-xSwPY3j-d44991uktwsPHIvuWu94M62ZerLwscik9adCfKGj0kRmsinzFaEIrr_Q88ELgtH3wX_b75jom3loSlO-4dsW8ZzpePofYJ_ZNxIN0fCSRx_qpJtG\",\"data\":{\"yello\":\"Yellow\"},\"priority\":10}"

I basically only get the onMessage method to trigger. Whenever I press the home button and trigger this from terminal, nothing happens. When I then reopen, the regular onmessage is triggered again. I am using the LocalNotificationsPlugin to display the silent notifications I want.

kalismeras61 commented 3 years ago

This sounds great however I've not been able to get this to work... I've modified my pubspec as the comment above and made the changes needed to my appdelegate.swift.

This is the message I'm sending to fcm

curl -X POST --header "Authorization: key=<MY_KEY>" \
    --Header "Content-Type: application/json" \
    https://fcm.googleapis.com/fcm/send \
    -d "{\"to\":\"fQ08m1akPULFqLDlNl4u_o:APA91bGjzqSuLdMlkPs-xSwPY3j-d44991uktwsPHIvuWu94M62ZerLwscik9adCfKGj0kRmsinzFaEIrr_Q88ELgtH3wX_b75jom3loSlO-4dsW8ZzpePofYJ_ZNxIN0fCSRx_qpJtG\",\"data\":{\"yello\":\"Yellow\"},\"priority\":10}"

I basically only get the onMessage method to trigger. Whenever I press the home button and trigger this from terminal, nothing happens. When I then reopen, the regular onmessage is triggered again. I am using the LocalNotificationsPlugin to display the silent notifications I want.

Add also "content_available": true, as a parameter.

tobiasjunsten commented 3 years ago

FIRMessagingRemoteMessage

@jengchtk I don't see any build errors in the log you provided. I only see warnings. Probably an error further down. Maybe you didn't import firebase_messaging as @isaiahtaylor pointed out is missing in the readme.

michaelgobbers commented 3 years ago

Add also "content_available": true, as a parameter.

Thx for your reply however, I'm still unable to get this to work. I first added the "content_available":true parameter but when I printed the resulting message it was added in a notification object. When I sent it like this "content-available":true it was not longer included in the message. However both messages only got print when I returned the app to the foreground. And the function that was being triggered was the onMessage function.

for completeness this is what I'm sending:

"{\"to\":\"f62qaqn57UnWmlIBNh53mE:APA91bHhfgSI6DU5k-ci32M56uC3NexuSCu7OXYC0dWR172DsxMxXXbJ7Qa7KB0DiEvLT4z8ZGA9v_ta3tZlS6eVx-7Rm4aDBexn-QEbzD2aJ34w7J92oEsMdhoHEHZ-tTVWp7G1nbHZ\",\"content-available\":true,\"data\":{\"yello\":\"Yellow\"}}"
kalismeras61 commented 3 years ago

Add also "content_available": true, as a parameter.

Thx for your reply however, I'm still unable to get this to work. I first added the "content_available":true parameter but when I printed the resulting message it was added in a notification object. When I sent it like this "content-available":true it was not longer included in the message. However both messages only got print when I returned the app to the foreground. And the function that was being triggered was the onMessage function.

for completeness this is what I'm sending:

"{\"to\":\"f62qaqn57UnWmlIBNh53mE:APA91bHhfgSI6DU5k-ci32M56uC3NexuSCu7OXYC0dWR172DsxMxXXbJ7Qa7KB0DiEvLT4z8ZGA9v_ta3tZlS6eVx-7Rm4aDBexn-QEbzD2aJ34w7J92oEsMdhoHEHZ-tTVWp7G1nbHZ\",\"content-available\":true,\"data\":{\"yello\":\"Yellow\"}}"

Use postman for testing. { "to" :"yourTokenId", "content_available": true, "priority" : "high", "data": { "example_data": "yourData" } }

michaelgobbers commented 3 years ago

Use postman for testing. { "to" :"yourTokenId", "content_available": true, "priority" : "high", "data": { "example_data": "yourData" } }

Same result I'm afraid.

kalismeras61 commented 3 years ago

Use postman for testing. { "to" :"yourTokenId", "content_available": true, "priority" : "high", "data": { "example_data": "yourData" } }

Same result I'm afraid.

Thats weird. Are you able to get notification messages?

michaelgobbers commented 3 years ago

Use postman for testing. { "to" :"yourTokenId", "content_available": true, "priority" : "high", "data": { "example_data": "yourData" } }

Same result I'm afraid.

Thats weird. Are you able to get notification messages?

yes I am but only when I resume it through the onMessage Callback.

This is how I set it up:

_initialize() {
    _firebaseMessaging.configure(
      onMessage: onMessage,
      onBackgroundMessage: showNotification,
    );
    _firebaseMessaging.requestNotificationPermissions(IosNotificationSettings(
        sound: true, alert: true, badge: true, provisional: false));
}

and these are my callback functions (first one is defined as a top level function so above the class definition that has the initialise method.

Future<dynamic> showNotification(Map<String, dynamic> message) async {
  print("background: $message");
  var initializationSettingsAndroid =
      AndroidInitializationSettings('@mipmap/rydoo_logo');
  var initializationSettingsIOS = IOSInitializationSettings(
    requestAlertPermission: true,
    requestBadgePermission: true,
    requestSoundPermission: true,
    defaultPresentAlert: true,
    defaultPresentBadge: true,
    defaultPresentSound: true,
  );
  var initializationSettings = InitializationSettings(
      initializationSettingsAndroid, initializationSettingsIOS);
  await plugin.initialize(initializationSettings);
  var androidDetails = AndroidNotificationDetails("0", "name", "description",
      priority: Priority.Max, importance: Importance.Max);
  var platformChannelSpecifics = NotificationDetails(
      androidDetails,
      IOSNotificationDetails(
          presentAlert: true, presentBadge: true, presentSound: true));
  return plugin.show(
    0,
    "ocr",
    jsonEncode(message),
    platformChannelSpecifics,
  );
}
Future onMessage(Map<String, dynamic> message) {
    print("front: $message");
    _streamController.add(message);
    return Future.value(0);
  }
michaelgobbers commented 3 years ago

I've been investigating further... I got it to work now. (log statement in console app) However I only got it to work by disabling a bunch of other plugins. I'm trying to figure out which one is causing this behaviour.

kalismeras61 commented 3 years ago

I've been investigating further... I got it to work now. (log statement in console app) However I only got it to work by disabling a bunch of other plugins. I'm trying to figure out which one is causing this behaviour.

Okay cool. Please let me know i am also using this PR.

Fatimamostafa commented 3 years ago

@misaelriojasftf You can test this pullrequest by adding this to your pubspec.yaml:

firebase_messaging:
    git:
      url: git://github.com/abilia/flutterfire.git
      ref: ios-background-support
      path: packages/firebase_messaging/

Is it supposed to work along with the existing onBackgroundMessage: _backgroundMessageHandler? Cause I've just added it to pubspec.yaml but it's not working? Anything else needs to be taken care of?

tobiasjunsten commented 3 years ago

Hi, could you advise how it would be possible to use plugins from within the static handler method? I get I/flutter ( 9944): MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences) when I try to use SharedPreferences from within that function.

Edit: this seems to be related to flutter/flutter#13937.

I'm using SharedPreferences in the background isolate so it's possible. I don't remember doing anything special to get it to work. Will try to see if I have some specific setting. I've had similar problems with other libraries though, like flutter_secure_storage.

michaelgobbers commented 3 years ago

I've been investigating further... I got it to work now. (log statement in console app) However I only got it to work by disabling a bunch of other plugins. I'm trying to figure out which one is causing this behaviour.

Okay cool. Please let me know i am also using this PR.

Alright so I've pinpointed the conflicting plugin. My project also contains a plugin for intercom https://pub.dev/packages/intercom_flutter It also offers push notifications. I had to disable method swizzling for this plugin as well.

Anyway thx for bearing with me here guys :)

fanatic75 commented 3 years ago

@tobiasjunsten , I have tried this pull, so when I add content_available to my payload, the app suddenly loses connection with the console and I cannot read logs anymore. `{ "to" : "",

 "content_available":true,

"data" : {

 "click_action": "FLUTTER_NOTIFICATION_CLICK",
 "title":"news",
 "body":"body",
 "newsID":200,
 "image_url":"https://media.wired.com/photos/5b899992404e112d2df1e94e/master/pass/trash2-01.jpg",

} }`

If I remove the content_available:true, it doesn't do anything in the background with this branch. but as soon as I add, it loses connection with the machine and I cannot read further logs to see If I have received the data message

isaiahtaylorhh commented 3 years ago

This is actually not relevant to this thread particularly, but since this PR is the only way to get background messages to work on iOS, I'll keep this here for anyone who followed the same path I did and then had these issues on Android.

Hey all, I'll share what we had to do on the plugin front to get this to work. I can't vouch for whether this will be relevant for everyone.

We initially had issues with SharedPreferences, getting this error:

I/flutter ( 9944): MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)
❯ flutter --version
Flutter 1.12.13+hotfix.9 • channel unknown • unknown source
Framework • revision f139b11009 (3 months ago) • 2020-03-30 13:57:30 -0700
Engine • revision af51afceb8
Tools • Dart 2.7.2

In our Application.kt, in the registerWith function, we had to add several plugins to get them to work in that static function:

package com.[removed].[removed];

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin
import io.flutter.view.FlutterMain
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin
import io.flutter.plugins.pathprovider.PathProviderPlugin
import io.flutter.plugins.firebaseanalytics.FirebaseAnalyticsPlugin

class Application : FlutterApplication(), PluginRegistrantCallback {

    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this);
        FlutterMain.startInitialization(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {
            FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
            SharedPreferencesPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin"));
            FirebaseAnalyticsPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebaseanalytics.FirebaseAnalyticsPlugin"));
        }
    }
}

Not sure if this would be different on newer version of Flutter.

fanatic75 commented 3 years ago

@isaiahtaylor, please don't post android config on a thread for iOS. There is no point here.

isaiahtaylorhh commented 3 years ago

I edited my comment to note that this is not necessarily relevant to this issue, it's just something that you might run into following this path.

tobiasjunsten commented 3 years ago

@tobiasjunsten , I have tried this pull, so when I add content_available to my payload, the app suddenly loses connection with the console and I cannot read logs anymore. `{ "to" : "",

 "content_available":true,

"data" : {

 "click_action": "FLUTTER_NOTIFICATION_CLICK",
 "title":"news",
 "body":"body",
 "newsID":200,
 "image_url":"https://media.wired.com/photos/5b899992404e112d2df1e94e/master/pass/trash2-01.jpg",

} }`

If I remove the content_available:true, it doesn't do anything in the background with this branch. but as soon as I add, it loses connection with the machine and I cannot read further logs to see If I have received the data message

@fanatic75 have you tried the console app on iOS? If you lose the connection with the app in the IDE-console you should still be able to see the logs from the app in the stand alone console app.

fanatic75 commented 3 years ago

@

@tobiasjunsten , I have tried this pull, so when I add content_available to my payload, the app suddenly loses connection with the console and I cannot read logs anymore. `{ "to" : "",

 "content_available":true,

"data" : {

 "click_action": "FLUTTER_NOTIFICATION_CLICK",
 "title":"news",
 "body":"body",
 "newsID":200,
 "image_url":"https://media.wired.com/photos/5b899992404e112d2df1e94e/master/pass/trash2-01.jpg",

} }` If I remove the content_available:true, it doesn't do anything in the background with this branch. but as soon as I add, it loses connection with the machine and I cannot read further logs to see If I have received the data message

@fanatic75 have you tried the console app on iOS? If you lose the connection with the app in the IDE-console you should still be able to see the logs from the app in the stand alone console app.

I have moved from data message to notification message which solves the background problem on IOS as well. OnLaunch is working once the app has been killed and onResume is working when the app is in background. Not using background message callback anymore on both platforms. Though there is definitely something weird as the connection should not get closed and the app is still running fine without crashing.

SKKurz commented 3 years ago

is it also possible to use the background message handling on an iOS Simulator? Or is this one of the things that only work on a proper iOS device?

oskara commented 3 years ago

is it also possible to use the background message handling on an iOS Simulator? Or is this one of the things that only work on a proper iOS device?

As far as I know this will only work on real devices.

owenkealey commented 3 years ago

Only conflicts are changelog and pubspec? This is a critical merge for my app.

csarigumba commented 3 years ago

Is this schedule for merge? We need this in our app.

tobiasjunsten commented 3 years ago

Hi @FirebaseExtended/invertase! Any chance you could take a look at this pull request? It's a really wanted feature for us and (as it seems) a lot of other people.

tobiasjunsten commented 3 years ago

Hi @Salakar! I tried to tag the @FirebaseExtended/invertase team as suggested here: FlutterFire Roadmap but don't seem to work. Any chance any of you could take a look at this pull request?