Flutter推送通知:无法在后台或应用关闭时接收通知。

12
我正在为Android和iOS开发一个Flutter应用程序。它具有通知功能,因此我实现了firebase_messaging API。我通过设备ID向特定设备发送通知,并向topics发送通知。
我正在测试topics的通知发送,在Android中工作得非常好。我按照上面提供的链接中的指南进行操作,也实现了iOS设置。但是在iOS中,当通知被发送时,仅当应用程序在前台时才收到通知。这意味着只能通过onMessage接收。如果应用程序在后台或关闭状态下,我看不到任何通知(我在控制台中打印)。但是当我重新打开应用程序时,通知会被打印出来。
以下是我的通知注册代码。
    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)
      );
    }

}

以下是我的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>

以下是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)
    }
}

正如其他SO答案中的一些人建议的那样,我从AppDelegate.swift代码中删除了以下部分并尝试过,但问题仍然存在。

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

我已经按照指南将苹果密钥文件上传到Firebase项目的云消息部分。

以下是我的JSON代码发送通知:

{
           "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"
        }

我的登录能力如下: enter image description here 我是iOS的新手,无法理解发生了什么。我认为就像在Android中一样,当应用程序在后台或关闭时,通知会自动出现在通知栏中。 更新 如@nandish的建议,我已将AppDelegate.swift文件更改如下:
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) {

     }
}

然后我遇到了以下错误

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

更新

当应用程序发出通知时,在前台,我从应用程序中获得以下响应。这仅在iPhone模拟器中发生,而不是在真实设备上。

{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}

你能截取一下你已经添加到目标中的功能吗? - mfaani
@Honey:抱歉回复晚了。我已经添加了截图,请查看。 - PeakGen
看起来还好。有时候只需取消选中并重新选中即可解决问题。另外,您何时注销设备令牌?也许您在后台操作时执行了注销用户的操作。 - mfaani
@Honey:是的,我无法将它们发送到真实设备。我曾经可以将它们发送到前台,但现在也不行了。实际上,我只是在打印它们,正如你所看到的那样。这就是我现在使用推送通知所做的全部。但是在iOS模拟器中,我可以将通知发送到前台。 - PeakGen
你的设备运行的是哪个iOS版本? - mfaani
显示剩余8条评论
4个回答

3

当他们在firebase_messaging 6.0.16中发现问题时,我遇到了非常类似的问题,现已更新至7.0.0

我调整了我的info.plist并进行了更改。

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

to

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

奇怪的是,我的通知在 firebase_6.0.9 中工作,因为我没有升级到更新的版本。

我的 appdelegate 代码如下:

@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)
  }
  }

这与您的稍有不同。


它已经起作用了。非常感谢!!我像您一样更改了info.plist,并更改了firebase_messaging版本6.0.9,它就可以工作了。在此之前,我使用的是版本7.0.3。 - khoi

1
首先,如果您使用Firebase Cloud Messaging进行通知,则Android将在应用程序打开或关闭时收到通知。但在iOS方面,iOS使用APN(Apple Push Notification)。
*- 您应该在真实设备上尝试。在iOS模拟器上,获取通知不稳定。在*
在您的Firebase控制台中,转到“设置>云消息> iOS应用程序配置”,然后添加来自Apple开发人员面板的APN标识密钥。您可以在this document上查看。
此外,您应该在通知负载中添加“content_available:true”。
这里有一个示例:
{
  "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"
  }
}

1
谢谢您的回复。我刚刚尝试了真实设备,发现根本没有收到任何通知。然而昨天当真实设备在前台时,我确实收到了通知。然后我使用了 content_available=true,但是没有效果。我认为我已经正确上传了APN,否则我不应该收到任何通知,对吧?当应用程序在前台时,我可以在模拟器上收到通知。 - PeakGen
好的,我明白了。实际上我不知道为什么。我在手机上尝试过,即使应用程序关闭时也无法收到通知。也许问题出在 Firebase 消息传递包上。 - FurkanKURT
2
"content_available": true 表示这是静默通知。请移除该标签。 - Jasmit
谢谢Jasmit,我不知道这个。对于错误的信息感到抱歉。 - FurkanKURT

0

你尝试过将传递给onMessage回调函数的消息数据进行转换吗?

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

0
对于后台通知,您可以尝试在目标项目下的“Signing & Capabilities”选项卡中单击“+ Capability”并启用远程通知来添加“Background Modes”功能,建议参考下面的苹果文档。

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

同时在你的AppDelegate.swift文件中,应该添加如下代码:

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) {

     }
}

谢谢您的回复,但是这个方法出现了错误。基本上它说您的方法仅适用于iOS 10及以上版本。我已经更新了我的问题并加入了这一点。 - PeakGen
似乎我没有收到真实设备的通知。我的测试设备是iPhone 6。 - PeakGen
你正在使用连接测试调试模式下的通知吗? - Jasmit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接