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}
13
  • Can you take a screenshot of the capabilities you've added to your target?
    – mfaani
    Commented Aug 5, 2020 at 21:15
  • @Honey: Sorry for the delay of reply. I have added the screenshot. Please check.
    – PeakGen
    Commented Aug 7, 2020 at 11:54
  • @Honey: Also it seems I am not getting notifications to the real device. My test device is iPhone 6.
    – PeakGen
    Commented Aug 7, 2020 at 12:22
  • 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 backgrounding
    – mfaani
    Commented Aug 7, 2020 at 12:42
  • 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 testing
    – mfaani
    Commented Aug 7, 2020 at 13:18

4 Answers 4

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.

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.3
    – khoi
    Commented Feb 20, 2021 at 8:05
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"
  }
}
4
  • 1
    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.
    – PeakGen
    Commented Aug 5, 2020 at 17:01
  • 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.
    – FurkanKURT
    Commented Aug 6, 2020 at 19:12
  • 2
    "content_available": true means it's silent notification. Please remove that tag.
    – Jasmit
    Commented Aug 11, 2020 at 9:30
  • Thank you Jasmit, I didn't know it. Sorry for bad information.
    – FurkanKURT
    Commented Aug 11, 2020 at 22:47
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) {

     }
}
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..
    – PeakGen
    Commented Aug 7, 2020 at 11:49
  • Also it seems I am not getting notifications to the real device. My test device is iPhone 6.
    – PeakGen
    Commented Aug 7, 2020 at 12:22
  • Are you testing notification in debug mode with connection?
    – Jasmit
    Commented Aug 11, 2020 at 9:40
0

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

data = Map<String, dynamic>.from(message['data']);

Not the answer you're looking for? Browse other questions tagged or ask your own question.