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

[firebase_messaging]: Receiving notifications in iOS background/terminated not working #12978

Open blue492 opened 1 week ago

blue492 commented 1 week ago

Is there an existing issue for this?

Are you aware of the differences between iOS and Android background message handling?

Do you have an active Apple Developer account?

Are you using a physical iOS device to test background messages?

Have you enabled "Remote Notifications" & "Background Mode" (Checking options for "Background Processing" & "Remote Notifications") in your app's Xcode project?

yes

Have you created an APNs key in your Apple Developer account & uploaded this APNs key to your Firebase console?

yes

Have you disabled method swizzling for Firebase in your app?

Yes and tried with YES or No, it not works

Are you sending messages to your app from the Firebase Admin SDK?

Yes, lock at my comments

Have you requested permission from the user to receive notifications?

Have you used the 'Console' application on your macOS device to check if the iOS device's system is throttling your background messages?

No I didn't use it

Additional context and comments

Iam sending notifications using Firebase Admin SDK for PHP and receiving in Flutter app using firebase and flutter_local_notification package.

My flutter app receiving notifications well in Android devices in foreground, background and terminated. iOS devices receiving notifications only in foreground. Notifications presents by flutter_local_notification package.

The problem that app not receiving notifications in ios device when the app in background and terminated.

Problem: To get notifications in ios device when the app in background and terminated I need to set 'alert' => $data, then the app receiving notifications, BUT notifications sends directly to the device not to the app via flutter_local_notification.

I want to present notifications in all modes and devices via flutter_local_notification.

Is there any thing which disable notifications receiving in ios device when the app in background and terminate? How to solve it?

Flutter: 3.22.2

firebase_core: ^3.1.0 firebase_messaging: ^15.0.1 firebase_database: ^11.0.1 flutter_local_notifications: ^17.1.2

PHP

    $data = [
            'title' => $title,
            'body' => $description,
            'id' => $id,
        ];

                    $message = CloudMessage::withTarget('token', $user['firebase_token'])
                        //  ->withNotification(Notification::create($title, $description))
                        ->withData($data);

                    if(strtolower($user['platform']) == 'android'){

                        $config = AndroidConfig::fromArray([
                            'ttl' => '3600s',
                            'priority' => 'normal',
                            'data' => [
                                'title' => $title,
                                'body' => $description,
                                'icon' => 'stock_ticker_update',
                                'color' => '#f45342',
                                'sound' => 'default',
                            ],
                        ]);

                        $message = $message->withAndroidConfig($config);
                    }
                    else if(strtolower($user['platform']) == 'ios'){
                        $config = ApnsConfig::fromArray([
                            'headers' => [
                                'apns-priority' => '5',

                            ],
                            'payload' => [
                                'aps' => [
                                    'contentAvailable'=> true,
                                //  'alert' => $data,
                                    'badge' => 1,
                                    'sound' => 'default',
                                  "mutable-content" => 1,
                                ],
                                'data' => $data,
                            ],
                        ]);

                        $message = $message->withApnsConfig($config);
                    }

                    try {
                        $messaging->send($message);
                    } catch (Exception $e) {
                        echo "Error sending message: " . $e->getMessage();
                    }

AppDelegate.swift

import UIKit
import Flutter
import flutter_local_notifications

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

       FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
           GeneratedPluginRegistrant.register(with: registry)
       }

  if #available(iOS 10.0, *) { 
     UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
   }
     GeneratedPluginRegistrant.register(with: self)
     return super.application(application, didFinishLaunchingWithOptions: launchOptions)
   }
}
Poloten commented 1 week ago

Hi blue492 please show how u subscibe on message. Dont forget u need call await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); before subsrcibe

bool isInitialApp = false;

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  if (!isInitialApp) await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 // logic
}

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

   if (!isInitialApp) await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  isInitialApp = true;
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
}
blue492 commented 1 week ago

Hi blue492 please show how u subscibe on message. Dont forget u need call await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); before subsrcibe

bool isInitialApp = false;

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  if (!isInitialApp) await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 // logic
}

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

   if (!isInitialApp) await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  isInitialApp = true;
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
}

Hi @Poloten I did already as your code, it's not works.

I added print in firebaseMessagingBackgroundHandler, no calls happen via onBackgroundMessage when I send the notification and app running in background.

It seems that ios disable notifications when the app in background/terminated

Poloten commented 1 week ago

blue492 u use firebase token ? I mean u need that:

 Future<String?> getToken() async {
    if (Platform.isIOS) {
      var apns = await firebaseInstance.getAPNSToken();
      debugPrint('apns $apns ');
    }

    var firebase = await firebaseInstance.getToken();

    debugPrint('token $firebase');
    return firebase;
}

and check FirebaseMessaging.onMessage.listen(showPushAppRunning); this fire if app is open.

blue492 commented 1 week ago

blue492 u use firebase token ? I mean u need that:

 Future<String?> getToken() async {
    if (Platform.isIOS) {
      var apns = await firebaseInstance.getAPNSToken();
      debugPrint('apns $apns ');
    }

    var firebase = await firebaseInstance.getToken();

    debugPrint('token $firebase');
    return firebase;
}

and check FirebaseMessaging.onMessage.listen(showPushAppRunning); this fire if app is open.

Do you mean that I should use apns token (getAPNSToken) when it is ios device? and getToken() in android? I tested apns now for ios but it not works in FirebaseMessaging.onMessage.listen, no calls happen

I didn't understand what you mean with apns variable in your example if you don't use it anywhere in the function.

Poloten commented 1 week ago

blue492 u use firebase token ? I mean u need that:

 Future<String?> getToken() async {
    if (Platform.isIOS) {
      var apns = await firebaseInstance.getAPNSToken();
      debugPrint('apns $apns ');
    }

    var firebase = await firebaseInstance.getToken();

    debugPrint('token $firebase');
    return firebase;
}

and check FirebaseMessaging.onMessage.listen(showPushAppRunning); this fire if app is open.

Do you mean that I should use apns token (getAPNSToken) when it is ios device? and getToken() in android? I tested apns now for ios but it not works in FirebaseMessaging.onMessage.listen, no calls happen

I didn't understand what you mean with apns variable in your example if you don't use it anywhere in the function.

No, in Apple device you shoud first call await firebaseInstance.getAPNSToken(); and then call await firebaseInstance.getToken() and use firebaseToken. If you don't call the method getAPNSToken() - nothing will work.

blue492 commented 1 week ago

blue492 u use firebase token ? I mean u need that:

 Future<String?> getToken() async {
    if (Platform.isIOS) {
      var apns = await firebaseInstance.getAPNSToken();
      debugPrint('apns $apns ');
    }

    var firebase = await firebaseInstance.getToken();

    debugPrint('token $firebase');
    return firebase;
}

and check FirebaseMessaging.onMessage.listen(showPushAppRunning); this fire if app is open.

Do you mean that I should use apns token (getAPNSToken) when it is ios device? and getToken() in android? I tested apns now for ios but it not works in FirebaseMessaging.onMessage.listen, no calls happen I didn't understand what you mean with apns variable in your example if you don't use it anywhere in the function.

No, in Apple device you shoud first call await firebaseInstance.getAPNSToken(); and then call await firebaseInstance.getToken() and use firebaseToken. If you don't call the method getAPNSToken() - nothing will work.

I tested, iam getting token for apns and firebase, but i still getting notifications only when app is open call to FirebaseMessaging.onMessage.listen,

No calls/triggers to FirebaseMessaging.onBackgroundMessage when app in the background firebaseMessagingBackgroundHandler

Poloten commented 1 week ago

try to send with Notification object (title, body) https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#resource:-message

blue492 commented 1 week ago

try to send with Notification object (title, body) https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#resource:-message

'alert' object works, not notification ref: https://firebase-php.readthedocs.io/en/stable/cloud-messaging.html

The problem still notifications send directly to device not trigger on FirebaseMessaging.onBackgroundMessage
firebaseMessagingBackgroundHandler

in firebaseMessagingBackgroundHandler I use flutter_local_notifications to present the message

firebaseMessagingBackgroundHandler trigger only in Android

Poloten commented 1 week ago

can u show screenshot from xcode "Background modes"

blue492 commented 1 week ago

image

Poloten commented 1 week ago

To check the status of your push notification, please follow the steps below.

  1. Open the Console app on your Mac
  2. Select your iPhone from the devices list on the left hand side
  3. Filter messages by typing in your Bundle ID (e.g. The Firebase Messaging example app bundle ID is 'io.flutter.plugins.firebase.messaging') into the search box and pressing enter. in search field you can write your bundle id 'com.adasd.app' and 'flutter'.
  4. Press the clear button to clean the history.
  5. Now send the message to your device

That's logs may help you

blue492 commented 1 week ago

@Poloten yes I can see in Console app that the notification is coming with the data I sent, but why FirebaseMessaging.onBackgroundMessage don't trigger ?

Poloten commented 1 week ago

@Poloten yes I can see in Console app that the notification is coming with the data I sent, but why FirebaseMessaging.onBackgroundMessage don't trigger ?

did you get permission ?

    await firebaseInstance.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: true,
      provisional: false,
      sound: true,
    );
blue492 commented 1 week ago

@Poloten yes I can see in Console app that the notification is coming with the data I sent, but why FirebaseMessaging.onBackgroundMessage don't trigger ?

did you get permission ?

    await firebaseInstance.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: true,
      provisional: false,
      sound: true,
    );

yes I get permission

blue492 commented 1 week ago

Here are some logs from Console app, hope it can help

Received incoming message on topic .com.domain.name at priority 10
[.com.domain.name] Received remote notification request xxxx-xxxx [ waking: 1, hasAlertContent: 0, hasSound: 1 hasBadge: 1 hasContentAvailable: 1 hasMutableContent: 1 pushType: Alert]
[.com.domain.name] Process delivery of push notification xxxx-xxxx
[.com.domain.name] Content-available push notifications are only supported on-device for iOS, watchOS, and tvOS
[.com.domain.name] Suppressing sound on user visible notification xxxx-xxxx because it has no alert and the app is in the background
[.com.domain.name] Badge can be set for notification xxxx-xxxx: 1 [ canBadge: 1 badgeNumber: 1 ]
[.com.domain.name] Saving notification 3D40-185A: NO [ hasAlertContent: NO, shouldPresentAlert: YES settingsShouldSave: YES]
Poloten commented 1 week ago

I dont't use flutterlocalnotification. But in documentation it require:

You need to configure a top level or static method which will handle the action:

@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
  // handle action
}
await flutterLocalNotificationsPlugin.initialize(
    initializationSettings,
    onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) async {
        // ...
    },
    onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);

If nothing happend try to add FirebaseApp.configure(). And check what maybe your application have another error

 import UIKit
import Flutter
import FirebaseCore

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FirebaseApp.configure()
blue492 commented 5 days ago

I have already implemented code above, I got error with FirebaseApp.configure() so I removed it.

It still same issue FirebaseMessaging.onBackgroundMessage don't trigger in ios.