Flutter Push Notifications: Unable to receive notifications in background or when app is closed
Asked Answered
A

4

12

I am developing a flutter app for both Android and iOS. It has notifications, so I did implement the firebase_messaging API. I am sending notifications to certain devices by device ID and sending notifications to topics as well.

I am testing the topics notification sending, which is working 100% fine in Android. I followed the guide in above provided link, implemented iOS setups as well. But in iOS, when the notification is sent, it is being received by app only if it is in foreground. Which means, only via onMessage. If the app is in background or closed, I see no notification (I am printing it in console). But when I reopen the app, the notification gets printed.

Below are my notification register code

    FirebaseMessagesImpl msg = FirebaseMessagesImpl();
         msg.requestPermissions();
    
    //Get configured with firebase messaging to recieve messages
    msg.getMessage().then((_) {
     msg.register().then((String token) {
     print(token);
    
     //Register to the `topic` so we get messages sent to the topic
     msg.topicRegister();
.........
}

FirebaseMessagesImpl

  import 'package:firebase_messaging/firebase_messaging.dart';

  import 'package:http/http.dart' as http;
  import 'dart:convert' as convert;

  class FirebaseMessagesImpl {
    final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

    String serverToken =
        "AAAAAAA:XXXXXXXXX-YYYYYYYY-ZZZZ-BBBBBBBB";

    Future<String> register() async {
      String token = "";
      _firebaseMessaging.getToken().then((t) {
        token = t;
        print("ha: " + t);
      });

      return token;
    }

    Future<void> getMessage() async {
      _firebaseMessaging.configure(
          onMessage: (Map<String, dynamic> message) async {
        print('on message $message');
        //setState(() => _message = message["notification"]["title"]);
      }, onResume: (Map<String, dynamic> message) async {
        print('on resume $message');
        // setState(() => _message = message["notification"]["title"]);
      }, onLaunch: (Map<String, dynamic> message) async {
        print('on launch $message');
        //setState(() => _message = message["notification"]["title"]);
      });
    }

    void topicRegister() {
    // _firebaseMessaging.subscribeToTopic("mobile_admin");
      _firebaseMessaging.subscribeToTopic("puppies");
    }



    void requestPermissions() async
    {
      await _firebaseMessaging.requestNotificationPermissions(
        const IosNotificationSettings(sound:true, badge:true, alert:true, provisional:false)
      );
    }

}

Below is my Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>FirebaseAppDelegateProxyEnabled</key>
    <false/>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>MY APP NAME</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>$(FLUTTER_BUILD_NAME)</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>$(FLUTTER_BUILD_NUMBER)</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>UIBackgroundModes</key>
    <array>
        <string>fetch</string>
        <string>remote-notification</string>
    </array>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
</dict>
</plist>

Below is AppDelegate.swift

import UIKit
import Flutter
import Firebase

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FirebaseApp.configure()
    GeneratedPluginRegistrant.register(with: self)
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
    
    override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

      Messaging.messaging().apnsToken = deviceToken
      super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
    }
}

As some people recommended in OTHER SO answers, I removed the following section from the AppDelegate.swift code and tried, still the same issue persists.

if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }

I HAVE uploaded the apple key file to Firebase project cloud messaging section as in the guide as well.

Below is my JSon code sending notifications

{
           "notification": {
              "body": "body",
              "title": "title"
           },
           "priority": "high",
           "data": {
            "body": "body",
              "title": "title",
              "click_action": "FLUTTER_NOTIFICATION_CLICK",
              "id": "1",
              "status": "done",
              "image": "https://ibin.co/2t1lLdpfS06F.png"
           },
           "to": "/topics/puppies"
        }

My Signin Capabilities are as follows

enter image description here I am new to iOS and can't figure out whats going on. I thought just like in Android, the notification will auto appear in notification bar when the app is in background or closed.

Update

As suggestion by @nandish, I changed the AppDelegate.swift file as below

import UIKit
import Flutter
import Firebase

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, UNUserNotificationCenterDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FirebaseApp.configure()
    GeneratedPluginRegistrant.register(with: self)
    UNUserNotificationCenter.current().delegate = self
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
    
    override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

      Messaging.messaging().apnsToken = deviceToken
      super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
    }
    
    // MARK: - UNUserNotificationCenterDelegate Method
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

     }
}

Then I ended up with the following error

UNUserNotificationCenter' is only available in iOS 10.0 or newer
        func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

Update

In foreground, I am getting the following response from the app when a notification is issued. Thats also only in iPhone Simulator, not in real device.

{from: /topics/puppies, status: done, id: 1, notification: {body: body, title: title, e: 1, tag: topic_key_9204266xxxxxxx535}, title: title, image: https://ibin.co/2t1lLdpfS06F.png, collapse_key: com.aaa.xxx, body: body, click_action: FLUTTER_NOTIFICATION_CLICK}
Audiophile answered 3/8, 2020 at 10:34 Comment(13)
Can you take a screenshot of the capabilities you've added to your target?Sextillion
@Honey: Sorry for the delay of reply. I have added the screenshot. Please check.Audiophile
@Honey: Also it seems I am not getting notifications to the real device. My test device is iPhone 6.Audiophile
Looks ok. Sometimes just unchecking and checking them again might fix it. Also when do you unregister the device token? Maybe you’re doing something that unregisters the user upon backgroundingSextillion
Btw what do you mean you’re not getting notifications to your real device? Push notifications are only delivered to a real device. Simulators can’t create a token with APNs...so only use real device for testingSextillion
@Honey: Yes, I am not getting them to the real device. I used to get it to foreground, but now not that either. Actually I am printing it, as you can see. Thats all I do with push notifications now. But with iOS simulator, I get the notifications to the foreground.Audiophile
@Honey: I only maintain the FCM Token generated by the Flutter. I DO not work with any other device token.Audiophile
@Honey: I made another update to the question, please check the last update made regarding the Json response I getAudiophile
What's the iOS version of your device?Sextillion
Let us continue this discussion in chat.Sextillion
@Honey iOS version 13.3Audiophile
Can you join the chat?Sextillion
hey , did you make an app id as explained in this guide firebase.google.com/docs/cloud-messaging/ios/… ?Neologize
T
3

i had a very similar issue when they discovered an issue in firebase_messaging 6.0.16 now its been updated to 7.0.0

i tweaked my info.plist and changed

<key>FirebaseAppDelegateProxyEnabled</key><false/>

to

<key>FirebaseAppDelegateProxyEnabled</key>
<string>false</string>

strangely enough my notifications work in firebase_6.0.9 as i didnt upgrade to the newer one.

my appdelegate is:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    //FirebaseApp.configure()
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  }

it is somewhat different to yours.

Tridactyl answered 7/10, 2020 at 5:30 Comment(1)
its worked Thank you so much!!. I change info.plist same like your and change firebase_messaging version 6.0.9 and its worked.Before this i'm using version 7.0.3Lanti
M
1

Firstly, if you use Firebase Cloud Messaging for notification, android will get the notification when app is open or closed. But on iOS side, iOS is using APN's (Apple Push Notification).

*- You should try on real device. On iOS emulators, it's not stable to get notifications. On *

On your firebase console go to Settings > Cloud Messaging > iOS Application Configuration then add your APN Identification Key from your Apple Developer panel. You can see on this document.

Also you should add "content_available": true on your notification payload.

There is an example here:

{
  "to": "/topics/topic_name",
  "content_available": true,
  "notification": {
    "title": "TITLE TEXT",
    "body": "BODY TEXT",
    "content_available": true
  },
  "data": {
    "body": "BODY TEXT",
    "title": "TITLE TEXT",
    "click_action": "FLUTTER_NOTIFICATION_CLICK"
  }
}
Masterful answered 5/8, 2020 at 15:54 Comment(4)
Thank you for the reply. Well, I just tried with real device, then noticed I'm not getting any notifications at all. However yesterday I got notifications in real device when its in foreground. Then I used the content_available=true and no good. I THINK I have uploaded the APN correctly, otherwise I am not supposed to get any notification at all, isn't it? I am getting notifications to the emulator when the app is in foreground.Audiophile
Ok I understand, well actually I don't know why. I tried on my phone and also I couldn't get the notification when app is closed. Maybe the problem is firebase messaging package.Masterful
"content_available": true means it's silent notification. Please remove that tag.Hepza
Thank you Jasmit, I didn't know it. Sorry for bad information.Masterful
R
0

For Background notifications , you can try adding "Background Modes" capability in by clicking on "+ Capability" on your target project under "Signing & Capabilities" tab and enable Remote notifications as suggested in below apple document.

https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app

Also in your AppDelegate.swift you should added as below:

class AppDelegate: FlutterAppDelegate , UNUserNotificationCenterDelegate {
      override func application(_ application: UIApplication,
          didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
       ....
       UNUserNotificationCenter.current().delegate = self
       ....
    }
    ....

    // MARK: - UNUserNotificationCenterDelegate Method
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

     }
}
Rozanne answered 5/8, 2020 at 21:10 Comment(3)
Thank you for the reply, but this gives error. bASICALLY it says your method is only for iOS 10 and above. I have updated my question with this..Audiophile
Also it seems I am not getting notifications to the real device. My test device is iPhone 6.Audiophile
Are you testing notification in debug mode with connection?Hepza
W
0

Have you tried casting the message data that gets passed to the onMessage callback,

data = Map<String, dynamic>.from(message['data']);
Wilma answered 30/10, 2020 at 15:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.