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.51k stars 3.92k forks source link

πŸ› [firebase_messaging] Background handler never invoked on iOS #6290

Closed mikeroneer closed 1 year ago

mikeroneer commented 3 years ago

Bug report

Describe the bug According to https://firebase.flutter.dev/docs/messaging/usage/#message-types, the background handler should be invoked for notification messages, data messages or a combination of both when the app is in the background or terminated. In our project, it is not invoked in any of those cases. For the sake of completeness it is worth to mention that the onMessage stream fires properly when the app is in foreground and also the notification handled by the FCM-SDK is shown when the app is in background/terminated. It's just the onBackgroundMessage which is never invoked.

We are aware of the content_available flag, however, the issue does not just relate to silent (data only) messages.

Steps to reproduce

Steps to reproduce the behavior:

  1. run the simplified code snipped below
  2. (optional) add a breakpoint in the background handler
  3. put the app into background/terminate it
  4. send a notification/data message via FCM (Firebase console or API call)
  5. result: "Handling a background message..." is not printed in the console, nor does the debugger stop in the _firebaseMessagingBackgroundHandler

Expected behavior

onBackgroundMessage is invoked when the app is in background or terminated.

Sample project

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  print('Handling a background message: ${message.messageId}');
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  final token = await FirebaseMessaging.instance.getToken();
  print('Push notification token: $token');

  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    print('Got a message whilst in the foreground!');
    print('Message data: ${message.data}');

    if (message.notification != null) {
      print('Message also contained a notification: ${message.notification}');
    }
  });
}

Edit: Payload (suggested by @markusaksli-nc)

We have tried quite a lot of different payload combinations, with both, containing a data object only and a notification object respectively. We have also tried addressing the recipient via a topic (which is our intended use case) as well as with the unique device token. Background modes (Background fetch and Remote Notifications) are enabled too. On the server side, we are using the Java SDK, however, we have also tried sending a POST request directly via Google's OAuth 2.0 Playground.

The following indicates a sample payload:

{
   "topic": "test-topic",
   "message": {
      "data": {
         "data-item": "my-data-item"
      },
      "apns": {
         "payload": {
            "aps": {
               "content-available": 1
            },
         }
      },
   },
}

Additional context

Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand ``` Doctor summary (to see all details, run flutter doctor -v): [βœ“] Flutter (Channel stable, 2.0.3, on macOS 11.3 20E232 darwin-x64, locale en-AT) [βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.3) [βœ“] Xcode - develop for iOS and macOS [βœ“] Chrome - develop for the web [βœ“] Android Studio (version 4.1) [βœ“] IntelliJ IDEA Ultimate Edition (version 2020.3.1) [βœ“] VS Code (version 1.56.2) [βœ“] Connected device (3 available) ```

Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand ``` Dart SDK 2.12.2 Flutter SDK 2.0.3 dependencies: - firebase_analytics 8.1.0 [firebase_analytics_platform_interface firebase_analytics_web firebase_core flutter meta] - firebase_core 1.2.0 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_crashlytics 2.0.4 [firebase_core firebase_core_platform_interface firebase_crashlytics_platform_interface flutter stack_trace] - firebase_messaging 10.0.0 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta] ... transitive dependencies: - firebase 9.0.1 [http http_parser js] - firebase_analytics_platform_interface 2.0.1 [flutter meta] - firebase_analytics_web 0.3.0+1 [firebase firebase_analytics_platform_interface flutter flutter_web_plugins meta] - firebase_core_platform_interface 4.0.1 [collection flutter meta plugin_platform_interface] - firebase_core_web 1.1.0 [firebase_core_platform_interface flutter flutter_web_plugins js meta] - firebase_crashlytics_platform_interface 3.0.4 [collection firebase_core flutter meta plugin_platform_interface] - firebase_messaging_platform_interface 3.0.0 [firebase_core flutter meta plugin_platform_interface] - firebase_messaging_web 2.0.0 [firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins js meta] ... ```

anushkapubudu commented 2 years ago

Same issue for me. I only tested for Android. An icon and message show in system bar but doesn't pop up. But using the flutter_local_notifications plugin, I fixed that. But problem is two notifications shows for one. One from my custom notification, another from default one. Anybody had same issue??

ffaridi1 commented 2 years ago

I just get it the background message handling in iOS to work with following : 1- The structure of notification should be same as what @TRiedel94 has mentioned above 2- For those who are getting issues with release mode, you have to make sure to build the App with an Adhoc profile with your phone's UDID in the profile 3- Make sure to add following in your info.plist

UIBackgroundModes
<array>
    <string>fetch</string>
    <string>remote-notification</string>
</array>

Thats it !! Make sure to do a clean build (in case of flutter , do flutter clean etc before building). I tried this method multiple times, and switch between the AdHoc profile and regular/automatic profile with Release build and it works all the time, so I am almost confident that this works.

MilanObrenovic commented 2 years ago

I am receiving a notification in background and terminated states on iOS, but the callback method is not invoked. Therefore i can't manipulate with data after the user clicks on the notification.

await Firebase.initializeApp(
  options: DefaultFirebaseOptionsDev.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

Is in my main and

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(
    options: DefaultFirebaseOptionsDev.currentPlatform,
  );

  print("Handling a background message: ${message.messageId}");
}

is a method outside of any class in main.dart

Any update on this?

TesteurManiak commented 2 years ago

Facing the same issue when running my app in profile or release mode. On iOS (v15.0.2), when the app is terminated I am correctly receiving the notification but the background handler is not executed to save the notification in my sharedprefs.

Flutter doctor

[βœ“] Flutter (Channel stable, 2.5.3, on macOS 11.6 20G165 darwin-x64, locale fr-FR)
    β€’ Flutter version 2.5.3 at /usr/local/share/flutter
    β€’ Upstream repository https://github.com/flutter/flutter.git
    β€’ Framework revision 18116933e7 (3 months ago), 2021-10-15 10:46:35 -0700
    β€’ Engine revision d3ea636dc5
    β€’ Dart version 2.14.4

[βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    β€’ Android SDK at /usr/local/share/android-sdk
    β€’ Platform android-31, build-tools 30.0.2
    β€’ ANDROID_HOME = /usr/local/share/android-sdk
    β€’ ANDROID_SDK_ROOT = /usr/local/share/android-sdk
    β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS
    β€’ Xcode at /Applications/Xcode.app/Contents/Developer
    β€’ Xcode 13.0, Build version 13A233
    β€’ CocoaPods version 1.11.2

[βœ“] Chrome - develop for the web
    β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 2020.3)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ Flutter plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
    β€’ Dart plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart
    β€’ Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)

[βœ“] IntelliJ IDEA Ultimate Edition (version 2018.3.5)
    β€’ IntelliJ at /Applications/IntelliJ IDEA.app
    β€’ Flutter plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
    β€’ Dart plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart

[βœ“] VS Code (version 1.63.2)
    β€’ VS Code at /Applications/Visual Studio Code.app/Contents
    β€’ Flutter extension version 3.18.1

[βœ“] Connected device (2 available)
    β€’ iPhone 8 (mobile) β€’ 78885141c40aff9180be20d79f343dee37f0b8b6 β€’ ios  β€’ iOS 15.0.2 19A404

Code

Background Handler

Future<void> firebaseMessagingBackgroundHandler(RemoteMessage msg) async {
  debugPrint("backgroundHandler: ${msg.notification?.title} - ${msg.data}");
  final notif = NotificationModel.fromDataMessage(msg.data);
  final instance = await SharedPreferences.getInstance();
  final oldNotifs = (instance.getStringList(
            "notifsHistoryStorageKey",
          ) ??
          [])
      .map<NotificationModel>(
        (e) =>
            NotificationModel.fromJson(jsonDecode(e) as Map<String, dynamic>),
      )
      .toList()
    ..add(notif);
  await instance.setStringList(
    "notifsHistoryStorageKey",
    oldNotifs.map<String>((e) => jsonEncode(e.toJson())).toList(),
  );
}

Firebase Initialization

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp();
  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

  runApp(
    BlocProvider.withBlocs(
      key: GlobalKey(),
      child: const MyApp(),
    ),
  );
}

Notifications Repository

class PushNotificationsImpl implements PushNotifications {
  static const fcmTopicsSelectionStorageKey = "fcm_topics_selection";
  static const _initializationSettingsAndroid =
      AndroidInitializationSettings("ic_launcher");

  static const _platformChannelSpecifics = NotificationDetails(
    android: AndroidNotificationDetails(
      "my_channel",
      "My notification channel",
      channelDescription: "Channel used to send notification.",
    ),
    iOS: IOSNotificationDetails(),
  );

  final FlutterLocalNotificationsPlugin localNotificationsPlugin;
  final SynchronousLocalStorage syncLocalStorage;

  PushNotificationsImpl({
    required this.localNotificationsPlugin,
    required this.syncLocalStorage,
  });

  final _firebaseMessaging = FirebaseMessaging.instance;

  bool _isReady = false;

  @override
  Future<bool> get isReady async {
    if (!_isReady) {
      _isReady = await _initialize();
    }
    return _isReady;
  }

  Future<bool> _initialize() async {
    try {
      // For iOS request permission first
      final _isAuthorized = await _requestiOSPermission();
      if (Platform.isIOS && !_isAuthorized) return _isAuthorized;

      FirebaseMessaging.onMessage.listen(_onMessage);
      FirebaseMessaging.onMessageOpenedApp.listen(_onMessageOpenedApp);

      await localNotificationsPlugin.getNotificationAppLaunchDetails();

      final initializationSettingsIOS = IOSInitializationSettings(
        onDidReceiveLocalNotification: (_, String? title, String? body, __) {
          // Display a dialog with the notification details, tap ok to go to
          // another page.
          showDialog(
            context: EmsNavigator.navigatorKey.currentContext!,
            builder: (context) => CupertinoAlertDialog(
              title: Text(title ?? ""),
              content: Text(body ?? ""),
              actions: [
                CupertinoDialogAction(
                  isDefaultAction: true,
                  child: const Text("Ok"),
                  onPressed: () => Navigator.pop(context),
                ),
              ],
            ),
          );
        },
      );

      final initializationSettings = InitializationSettings(
        android: _initializationSettingsAndroid,
        iOS: initializationSettingsIOS,
      );

      final initializationSucceeded = await localNotificationsPlugin.initialize(
        initializationSettings,
        onSelectNotification: (payload) {
          if (payload != null) {
            final decodedPayload = jsonDecode(payload) as Map<String, dynamic>;
            final notif = NotificationModel.fromJson(decodedPayload);
            EmsNavigator.pushNamed(
              NotifDetailsView.routeName,
              arguments: notif,
            );
          }
        },
      );

      return initializationSucceeded ?? false;
    } catch (e) {
      debugPrint(e.toString());
      return false;
    }
  }

  Future<void> _updateNotifs(
    RemoteMessage remoteMessage,
    NotificationModel notif,
  ) async {
    final notifsBloc = BlocProvider.master<NotificationsBloc>();
    await firebaseMessagingBackgroundHandler(remoteMessage);
    await notifsBloc.reloadNotifs();
  }

  void _onMessage(RemoteMessage msg) {
    debugPrint("onMessage: ${msg.notification?.title} - ${msg.data}");
    if (msg.data.isNotEmpty) {
      final notif = NotificationModel.fromDataMessage(msg.data);
      _updateNotifs(msg, notif).then(
        (_) {
          if (Platform.isAndroid) {
            showNotification(
              1,
              notif.title,
              notif.body.toString(),
              payload: jsonEncode(notif.toJson()),
            );
          }
        },
      );
    }
  }

  void _onMessageOpenedApp(RemoteMessage msg) {
    debugPrint("onMessageOpenedApp: ${msg.notification?.title} - ${msg.data}");
    final notifsBloc = BlocProvider.master<NotificationsBloc>();
    notifsBloc.reloadNotifs().then(
      (_) {
        final notif = notifsBloc.notifications?.firstWhereOrNull(
          (e) =>
              e.title == msg.notification?.title &&
              e.body == msg.notification?.body,
        );
        if (notif != null) {
          EmsNavigator.pushNamed(
            NotifDetailsView.routeName,
            arguments: notif,
          );
        }
      },
    );
  }

  Future<bool> _requestiOSPermission() async {
    NotificationSettings settings;
    if (Platform.isIOS) {
      settings = await _firebaseMessaging.requestPermission();
      return settings.authorizationStatus == AuthorizationStatus.authorized;
    }
    return false;
  }

  @override
  Future<void> showNotification(
    int id,
    String title,
    String body, {
    String? payload,
  }) async {
    final instanceReady = await isReady;
    if (!instanceReady) return;

    await localNotificationsPlugin.show(
      id,
      title,
      body,
      _platformChannelSpecifics,
      payload: payload,
    );
  }
}

Notification send with Postman

I'm sending 2 notifications, first one is a data message supposed to trigger the background handler which works when app is in background but not when terminated:

{
    "to": "{{to}}",
    "data": {
        "title": "{{title}}",
        "subtitle": "{{subtitle}}",
        "startDate": "{{startDate}}",
        "endDate": "{{endDate}}",
        "message": "{{message}}",
        "image": "{{image}}"
    },
    "content_available": true,
    "priority": "high"
}

The second is a classic notification message only used to display the notification banner (works correctly):

{
    "to": "{{to}}",
    "notification": {
        "title": "{{title}}",
        "body": "{{subtitle}}\n{{startDate}} - {{endDate}}\n\n{{message}}",
        "image": "{{image}}"
    }
}
misici234 commented 2 years ago

Is there any news on this issue? This is a serious showstopper for my development.

misici234 commented 2 years ago

Why are there no comments after so long time? Can anyone say what's going on with this problem?

sikking100 commented 2 years ago

for me, it work when you add content_available key in your json, and the value must be true. It will trigger the FirebaseMessaging.onBackgroundMessage on iOS.

{
  "to": "fcmToken",
  "notification": {
    "body": "sample body",
    "content_available": true,
    "title": "hello"
  }
}
madduruashok commented 2 years ago

for me, it work when you add content_available key in your json, and the value must be true. It will trigger the FirebaseMessaging.onBackgroundMessage on iOS.

{
  "to": "fcmToken",
  "notification": {
    "body": "sample body",
    "content_available": true,
    "title": "hello"
  }
}

From this link : https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#notification Seems there is no content-available field inside notification. could you confirm once?

sikking100 commented 2 years ago

for me, it work when you add content_available key in your json, and the value must be true. It will trigger the FirebaseMessaging.onBackgroundMessage on iOS.

{
  "to": "fcmToken",
  "notification": {
    "body": "sample body",
    "content_available": true,
    "title": "hello"
  }
}

From this link : https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#notification Seems there is no content-available field inside notification. could you confirm once?

You should check this website https://firebase.flutter.dev/docs/messaging/usage to be precise, you can read in the Low priority messages section of that documentation.

madduruashok commented 2 years ago

https://firebase.flutter.dev/docs/messaging/usage

Thank you

for me, it work when you add content_available key in your json, and the value must be true. It will trigger the FirebaseMessaging.onBackgroundMessage on iOS.

{
  "to": "fcmToken",
  "notification": {
    "body": "sample body",
    "content_available": true,
    "title": "hello"
  }
}

From this link : https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#notification Seems there is no content-available field inside notification. could you confirm once?

You should check this website https://firebase.flutter.dev/docs/messaging/usage to be precise, you can read in the Low priority messages section of that documentation.

Thank you, but as I said, content-available flag should be in aps payload as mentioned in the doc you posted too but not in notification dictionary as you mentioned.

sikking100 commented 2 years ago

https://firebase.flutter.dev/docs/messaging/usage

Thank you

for me, it work when you add content_available key in your json, and the value must be true. It will trigger the FirebaseMessaging.onBackgroundMessage on iOS.

{
  "to": "fcmToken",
  "notification": {
    "body": "sample body",
    "content_available": true,
    "title": "hello"
  }
}

From this link : https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#notification Seems there is no content-available field inside notification. could you confirm once?

You should check this website https://firebase.flutter.dev/docs/messaging/usage to be precise, you can read in the Low priority messages section of that documentation.

Thank you, but as I said, content-available flag should be in aps payload as mentioned in the doc you posted too but not in notification dictionary as you mentioned.

yes, you are right. but when i place it in my notification payload, the background-handler works fine. Maybe i should check my code again later.

misici234 commented 2 years ago

Here is a sample of C# code which worked for me

     var message = new Message()
      {
        Data = new Dictionary<string, string>()
        {
          ["title"] = "You have the alerts".ToUpper(),
          ["body"] = "Tap to open Aura and see the details.".ToUpper(),
          ["click_action"] = "FLUTTER_NOTIFICATION_CLICK",
        },
        Topic = null,
        Apns = new ApnsConfig()
        {
          Aps = new Aps()
          {
            ContentAvailable = true,
            Sound = "default",     // important to produce default notification sound in iOS
          },
          Headers = new Dictionary<string, string>()
          {
            ["apns-collapse-id"] = "aura_alerts",
            ["apns-push-type"] = "background",
            ["apns-priority"] = "5", // Must be `5` when `contentAvailable` is set to true.
            ["apns-topic"] = "io.flutter.plugins.firebase.messaging", // bundle identifier
          },
        }
      }

On iOS side I referenced firebase_core: ^1.12.0 firebase_messaging: ^11.2.6 in pubspec.yaml

enorenio commented 2 years ago

I am facing the same issue. When app is in paused state (AppLifecycleState.paused) the notification comes to the device (as seen from Notification Center), but the handler never invokes. No problems with foreground notification though.

firebase_core: ^1.12.0 firebase_messaging: ^11.2.6

sikking100 commented 2 years ago

I am facing the same issue. When app is in paused state (AppLifecycleState.paused) the notification comes to the device (as seen from Notification Center), but the handler never invokes. No problems with foreground notification though.

firebase_core: ^1.12.0 firebase_messaging: ^11.2.6

maybe you can try my advice above, add content-available inside your push notification data.

enorenio commented 2 years ago

I am facing the same issue. When app is in paused state (AppLifecycleState.paused) the notification comes to the device (as seen from Notification Center), but the handler never invokes. No problems with foreground notification though. firebase_core: ^1.12.0 firebase_messaging: ^11.2.6

maybe you can try my advice above, add content-available inside your push notification data.

When I add content-available, the handler still doesn't invoke. But also the notification doesn't appear in Notification Center.

aytunch commented 2 years ago

@enorenio may I ask from where do you send the Push notification from? Is it from Firebase or somewhere else?

enorenio commented 2 years ago

@enorenio may I ask from where do you send the Push notification from? Is it from Firebase or somewhere else?

I send notifications from Postman (because this way I can provide additional fields in the payload) I use POST https://fcm.googleapis.com/fcm/send url to post notifications (with Authorization and Content-Type headers, and body as json)

aytunch commented 2 years ago

@enorenio Just like I suspected. This problem does not happen when push notifications are sent from Firebase FCM. Only when sent from other services. We are having the same issue trying to initiate a PN from a chat service called ConnectyCube. I guess package maintainers don't give much importance for push notifications coming from other places which is not acceptable. This issue blocks a lot of people. We just want to handle all the Push notifications our app receives from FCM and other places.

enorenio commented 2 years ago

@enorenio Just like I suspected. This problem does not happen when push notifications are sent from Firebase FCM. Only when sent from other services. We are having the same issue trying to initiate a PN from a chat service called ConnectyCube. I guess package maintainers don't give much importance for push notifications coming from other places which is not acceptable. This issue blocks a lot of people. We just want to handle all the Push notifications our app receives from FCM and other places.

Do you mean it works only if you send notification from Notification Composer in Firebase FCM? In my case it does not work at all (not from Notification Composer, not from Postman and not even from server side)

geoox commented 2 years ago

I am experiencing the same problem: the background handler function is not getting called on iOS (the others are working fine). Of course, the handler function is globally accessible and it is called at the right time. My server payload looks like this: const message = { notification: { title: notifTitle, body: notifBody, }, data: notifData, topic: topic, }; I have also tried to modify the server payload to contain apns details as others suggested, to no avail: const message = { notification: { title: notifTitle, body: notifBody }, data: { click_action: 'FLUTTER_NOTIFICATION_CLICK', body: JSON.stringify(notifData) }, topic: topic, apns: { payload:{ aps: { 'content-available': true }, headers:{ 'apns-push-type': 'alert', 'apns-priority':'5' }, } }, }; Did anyone find a solution for this?

misici234 commented 2 years ago

Here is a C# code of a message which works for me with FirebaseAdmin NuGet package

      var message = new Message()
      {
        Data = new Dictionary<string, string>()
        {
          ["title"] = "Title here",
          ["body"] = "Message text here",
          ["click_action"] = "FLUTTER_NOTIFICATION_CLICK",
        },
        Topic = null,
      };

        try
        {
          message.Token = device.DeviceToken;
            message.Apns = new ApnsConfig()
            {
              Aps = new Aps()
              {
                ContentAvailable = true,
                Sound = "default",     // important to produce default notification sound in iOS
              },
              Headers = new Dictionary<string, string>()
              {
                ["apns-collapse-id"] = "aura_alerts",
                ["apns-push-type"] = "background",
                ["apns-priority"] = "5", // Must be `5` when `contentAvailable` is set to true.
                ["apns-topic"] = "io.flutter.plugins.firebase.messaging", // bundle identifier
              },
            };
          }
icecandy commented 2 years ago

Well I seem to have solved this issue in my scenario. Background iOS notifications when the app is in the background and when the app is closed.

This DOES NOT WORK:

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
   ...
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

This DOES WORK:

void _handleMessage(RemoteMessage message) {
   ...
}
//for messages when the app is closed
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
    if (initialMessage != null) {
      _handleMessage(initialMessage);
    }
//for messages when the app is in the background
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);

The latter method I found on the official Flutterfire page: https://firebase.flutter.dev/docs/messaging/notifications/

FirebaseMessaging.onBackgroundMessage() definitely did work at some point in the past (I last tested in August 2021). it looks like a change in flutter notifications and an inconsistency in documentation, because using FirebaseMessaging.onBackgroundMessage() is definitely recommended/explained at other places in the flutter documentation. I have noticed this before several times, where the flutter documentation seems to get out of sync or out-of-date. Particularly with any firebase stuff which changes fundamentally all the time.

iOS 14.6 Flutter version 2.10.0 firebase_core: ^1.13.1 firebase_messaging: ^11.2.8 I also do have the following in my info.plist file:

<key>UIBackgroundModes</key>
    <array>
        <string>fetch</string>
        <string>remote-notification</string>
    </array>

which I believe is a requirement.

PS I am just testing notifications by using the firebase CMS (I haven't tried using the API or Postman). This is sending a trivial notification with data: {'action': '123'}

jorgeroncero commented 2 years ago

@icecandy your approach, replacing "onBackgroundMesage" with "onMessageOpenedApp" does not solve the issue.

onMessageOpenedApp handles the notification ONLY when the user tap the system notification and therefore open the app with it.

onBackgroundMessage should handle the notification as soon as it arrives to the system, to do any background processing on our app. This is what is working then the app is in background, but not when it is terminated.

icecandy commented 2 years ago

Well i did say "in my scenario" which seemed to be similar to other peoples. With regard to notifications, I don't see how it is possible to do any processing in the background, when the app is closed, without actually opening the app - which is the normal route when using notifications, especially for doing something like "deep linking". But maybe I'm missing something?

scalz commented 2 years ago

@icecandy I confirm too, that onBackgroundMesage handler works well for android but not for ios. in onBackgroundMesage you can for example store notification in sharedpref or db, display a customized local notification, without opening app. Deeplinking is not related, as it depends on your navigation implementation. I'm waiting patiently for the release of the FCM companion plugin from Awesome Notification, as it will be a complete solution for notification, instead of using separate third parties packages, thus you don't know which package is the culprit in a project, and I don't think anyone use only one package in his project.. also when your app is supposed to display only customized notifications and it doesn't work in background, some users can give bad reviews to your app

icecandy commented 2 years ago

@scalz actually it seems that I was right. When ios receives a notification, to process anything in onBackgroundMessage, it does open the app, but only in the background. So not necessarily visible to the user, unless the user chooses to select the notification and it can then open the app fully (or actually, as far as I understand, bring the app from the background to the foreground). Deeplinking is completely related. Deeplinking is when a notification appears that can link to something inside the app, so again some background processing and then opening the app and navigating to a certain screen or place in the app. Also, you say "display a customized local notification" without opening the app. This is completely inconsistent. A local notification is a notification in the app when it is open!

scalz commented 2 years ago

@icecandy
sorry maybe my bad english, but where did I say that it does not need any additional flutter engine/isolate/opening app for receiving notifications ? I just said that onBackgroundMessage doesn't work on iOS, since almost a year unfortunately, whereas it is working for Android. You said we should use onMessageOpenedApp as callback when app is in background (which is not right) or maybe I misunderstood. This issue is about Background handler, not about getting message when opening app. Regarding deeplink, it can be used with nfc, adb etc, even if app is killed it will open your app and redirect to the right page, this was what I was thinking when I said that :) So, indeed It's possible to use deeplinks without displaying notifications.. Finally, I mentioned "display a customized local notification" without opening the app", again I shortened what I meant. I meant, as onBackgroundMessage is not working on iOS, and as a customized notif is a local notif which can only be generated in this callback when app is in background, thus it is not possible to display one when app is in background (without opening app/foreground). And when you just use customized notification, with "data" only instead of "notification" (because local notif packages use data for custom settings and if you add "notification" you get duplicates), then it can become problematic because nothing is displayed. I agree with you there is no magic. Of course it could be some conflicts with others packages, but like I said who uses only one package in his project, especially for flutter notifications unless you just don't need to customize them. Do you use any "local" notification package with fcm package, with "data" only in json, on iOS? Try to generate custom local notification in onBackground. Do you have success when app is in background for some time ? If so, could you share your solution plz. If not, is this a normal behaviour and how others well known apps are doing it ?

aytunch commented 2 years ago

@scalz I totally agree with you. We have a chat module in our app and we are using a third party chat service provider which uses XMPP protocol. So when a user is sent a message, this service sends the push notification.

We have no problem iOS displaying the notification in foregorund and background. But the onMessageOpenedApp and onBackgroundMessage don't fire for these notifications. This has been like that for years unfortunately.

Firebase Messaging should be able to allow parsing notifications originating from other sources than FCM.

EDIT: are you using a 3rd party push sender too?

scalz commented 2 years ago

@aytunch we are using our own backend for sending directly to fcm. onBackgroundMessage works perfectly fine on android. but not for ios. so as a workaround, I use "notification" in json for ios in background as this is always displayed no matter onBackground is working or not, but ios users can't have action buttons, sexy custom notif etc.. while in background because this part is handled by local notification packages (awesome notification) which require "data" with custom settings in json handled in onBackground... Also, as onBackground is not fired, then sharedpref/db storage is not called, and no notifications history to display in app when opening it. Well, even if my ios users are eager to get this working (each update they ask whether it is fixed), I'm patiently waiting for awesome notification fcm companion plugin and will switch when it will be available (as compatibility with fcm package as been deprecated in 0.7 beta)

loic-hamdi commented 2 years ago

Any update on this? It is crucial to fix it asap please

mariopepe commented 2 years ago

I am having this issue as well iOS 15

bilalgodesto commented 2 years ago

Any update on this issue

inclu-cat commented 2 years ago

I hope this issue is resolved soon. If the official documentation had mentioned this issue, I would have been put off adopting FlutterFire. Please mention it in the documentation to reduce the number of people who have the same experience?

arthas1888 commented 2 years ago

same problem for me

ronpetit commented 2 years ago

For everyone that still gets here, I solve the problem by updating my APNS configuration over my notification server, I'm using the firebase admin library with python, and a message example is like this

admin_message = messaging.Message(
        notification=messaging.Notification(
                title="Nuevo usuario esperando por Rol",
                body=f"El usuario {name.capitalize()} se ha registrado y esta a la espera de asignaciΓ³n de roles"
            ),
        data={
            'type': 'new_user',
            'username': f'{name.capitalize()}',
            'message': f"El usuario {name.capitalize()} se ha registrado y esta a la espera de asignaciΓ³n de roles",
            'order': "null",
            'activity': 'null',
            'datetime': formatted_datetime
        },
        android=messaging.AndroidConfig(
            priority='high'
        ),
        apns=messaging.APNSConfig(
            headers={
                "apns-push-type": "alert",
                "apns-priority": "5"
            },
            payload=messaging.APNSPayload(
                aps=messaging.Aps(
                    content_available=True
                )
            )
        ),
        topic="admin"
    )

The key here is the APNS headers with apns-push-type = 'alert' and apns-priority = '5' along with the aps config for content_available = true.

With this, all my messages triggers the onBackgroundMessage over iOS, however, this means that messages send for example, with the Firebase console does not trigger the onBackgroundMessage method, or at least I don't know how to add these APNS settings

This is with python, but APNS is a native settings of the Apple notification system, so any method should be able to send these settings

janhelwich commented 2 years ago

My use case is triggering calls. What worked for me: using the VoIP functionality of APN. I am using firebase for messaging, but it doesn't support it. However I now trigger the call through a firebase function and using the Node libraries for APN which is working flawlessly. There is a good description how to do it with Node here.

One thing I needed some time to figure out is that the .setup call has to be done quite early (I think before the first thing is shown to the user). Otherwise when the app is killed and needs to be spinned up, it fails silently (can be seen in the logs on the device). and very soon the device is not letting any background messages through anymore as the policy is that they must not crash (and show a call very soon after recieving of the message).

DoisLucas commented 2 years ago

same problem for me

russellwheatley commented 2 years ago

Hey @mikeroneer, I've been debugging this issue and I've come to the realisation that messaging works best when the manual installation step is taken for setting up iOS (i.e. GoogleService-Info.plist file added to the Runner in your Xcode project).

We're looking to add this as an automatic insertion using the flutterfire CLI which is in progress, but this will take time to implement. Here are the Firebase instructions for adding the GoogleService-Info.plist file in the meantime.

mariopepe commented 2 years ago

Hey @russellwheatley is this issue added on the documentation of the FlutterFire CLI in a very evident position?

petrnymsa commented 2 years ago

I am really confused. In earlier versions of docs there was info about manually adding GoogleService-Info.plist, even info about manual setup for Android for crashlytics. Old "FlutterFire" docs were merged to Firebase site and this information is gone. I've already did some workaround with https://github.com/firebase/flutterfire/issues/8376...

Right now yesterday I've migrated to FlutterFireCLI and removed manually added GoogleService-Info.plist and now I am reading that it will probably needed to get it back.

So what the right approach? Which docs are source of truth?

arthas1888 commented 2 years ago

Hey @mikeroneer, I've been debugging this issue and I've come to the realisation that messaging works best when the manual installation step is taken for setting up iOS (i.e. GoogleService-Info.plist file added to the Runner in your Xcode project).

We're looking to add this as an automatic insertion using the flutterfire CLI which is in progress, but this will take time to implement. Here are the Firebase instructions for adding the GoogleService-Info.plist file in the meantime.

I really did it without success, which version are you using?

inclu-cat commented 2 years ago

I added GoogleService-Info.plist to the Runner, but the result was not changed. Is there anything else I should do?

Flutter 2.10.3 firebase_core: ^1.14.1 firebase_messaging: ^11.2.13

bilalgodesto commented 2 years ago

Same here. added GoogleService-Info.plist to the Runner but no effect

UsamaKarim commented 2 years ago

I managed to get it work by adding "content-available": true. It was an easy fix though.

Here is my payload example in Cloud Functions

{
    topic: topic,
    notification: {
      title: title,
      body: body,
    },
    android: {
      notification: {
        sound: "default",
      },
    },
    apns: {
      payload: {
        aps: {
          "content-available": true,   <----- HERE
          sound: "default"
        },
      },
    },
  }
bilalgodesto commented 2 years ago

This will not always work. It will work one day and stop working completely

russellwheatley commented 2 years ago

@bilalgodesto this is nothing to do with FlutterFire I'm afraid, and is a result of how iOS operating system chooses to pass messages forward via the delegate handler. If too frequent or too many resources under use for example, it might never be received.

williamrech commented 2 years ago

Did anyone find a definitive solution to this problem? onBackgroundMessage works fine on Android with the app opened or closed, but on iOs it works only with the app open.

inclu-cat commented 2 years ago

Hi, @russellwheatley. Thanks for addressing this problem. When I add GoogleService-Info.plist, should I remove firebase_options.dart and relevant code? Or should I just add the GoogleService-Info.plist?

AymanMubark commented 2 years ago

just i added

"content_available": true for notifications work for me

{

"notification":{
"title":"mytitle", "body":"mybody", "content_available": true }, "to":"token", }

inclu-cat commented 2 years ago

@AymanMubark Hi, my customer is using the Firebase Console to send messages. Do you know how to set content_available to true in Firebase Console? Does your app's onBackgroundMessage get called when you send a message from the Firebase Console? ( I am testing in a debugging environment.)