iOS 模拟器或物理设备未收到 FCM 数据消息
FCM data messages not being received by iOS Simulator or physical device
我正在从事的一个项目需要使用 FCM 推送通知和消息进行 VoIP 控制等。
问题:
我的 iOS 物理设备 收到推送通知,但 仅当应用程序处于后台或关闭时 NOT 在前台时。此外,它 根本不接收任何 fcm 消息 (数据)通知,例如只有数据而没有通知的消息 title/body 即 VoIP 状态消息。
我的设置步骤如下(可以找到我遵循的详细设置和一般流程here):
注册一个(Info.plist
)(见here)
下载并使用Xcode安装GoogleService-Info.plist
(参见here)
创建 APNS 密钥并启用推送通知并将 .p8
文件上传到
Firebase(参见 here)
修改为Swift代码:
AppDelegate.swift
代码:
import UIKit
import Flutter
import Firebase // this is added, and (see further down)
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure() //add this before the code below
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
- 将功能添加到iOS
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>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
...
<key>NSCameraUsageDescription</key>
<string>Allows taking a self-portrait profile image or sending a photo while chating</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Allows using your location to make determining country & location picking easier (optional)</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allows using micrphone to chat with someone when making a call</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Allows opening files from gallery</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>bluetooth-central</string>
<string>external-accessory</string>
<string>fetch</string>
<string>location</string>
<string>processing</string>
<string>remote-notification</string>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<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>
注意:显示的背景模式 here 无法启用,而是我使用一组复选框来启用它们以启用 Background Fetch
等
- FCM - 监听并显示和notifications/messages使用一些通知(显示)服务,例如awesome notifications
实施遵循这些 guidelines:
前台消息:
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
...
});
后台消息:
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
问题扩展:
调试时,如果 iOS 应用程序在 foreground/background 中并且测试通知或有效的 VoIP 呼叫(FCM 数据消息)被发送到设备,应用程序永远不会收到或(也不会所需的断点是否命中)。
什么会导致 app/iOS 收不到这些 FCM 消息?
从 Firebase 云功能服务发送的示例 VoIP 消息:
// build notification on liked status
const payload = {
token: fcmToken,
data: {
uid: context.auth.uid,
room_id: roomId,
...
},
android: {
priority: 'high',
ttl: 0,
notification: {
priority: 'max',
defaultSound: true,
defaultVibrateTimings: true,
sticky: true,
clickAction: "FLUTTER_NOTIFICATION_CLICK",
}
},
notification: {
title: `Incoming call from ${name}`,
body: `Accept/Reject call`,
// icon: 'your-icon-url', // Add profile image URL here if necessary
// clickAction: 'FLUTTER_NOTIFICATION_CLICK' // required only for onResume or onLaunch callbacks
},
apns: {
payload: {
aps: {
category: "NEW_MESSAGE_CATEGORY",
contentAvailable: true,
}
}
},
headers: {
"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
},
};
await admin.messaging().send(payload);
原答案(更通用/对更多人有用):
FCM 不能在 iOS 模拟器上运行,所以问题已经解决了一半。对于物理设备,请查看一些 debugging steps 我在我工作的库中详细写的。
推送通知在 iOS 上出错的方式有多种。当您说:the app never receives
时,您的意思是您的 Flutter 应用程序没有收到消息吗?您的设备可能仍在接收它们。
- 在您的 mac 上打开
Console.app
,然后开始记录您的物理设备。过滤 dasd
个进程,你可以看到推送通知相关的日志,看看你的设备是否拒绝了推送通知,没有将它发送到你的应用程序。最有可能的是,您的消息结构存在问题。
- 你也可以尝试在Flutterfire firebase_messaging iOS代码内部的iOS端调试
didReceiveRemoteNotification
方法。 那 表示消息已被应用程序接收。
让我具体回答每个问题/疑虑 (3):
- 我的 iOS 物理设备收到推送通知,但只有当应用程序在后台或关闭时才如此但在前台时不会。
所以你想在前台显示通知?您需要实现 userNotificationCenter(_:willPresent:withCompletionHandler:)
,例如 Swift:
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if #available(iOS 14.0, *) {
completionHandler(.banner)
} else {
completionHandler(.alert)
}
}
为了在此处为您提供更多帮助,您需要设置委托。在上面实现的方法中打个断点,看看它是否真的被调用了。
- 在Swift中:
UNUserNotificationCenter.current().delegate = self;
- 在Objective-C中:
UNUserNotificationCenter.currentNotificationCenter.delegate = self;
- 此外,它根本不接收任何 fcm 消息(数据)通知,例如只有数据而没有通知的消息 title/body 即 VoIP 状态消息。
如果不设置通知字段,则需要设置优先级为5,推送类型为background
,contentAvailable
为true
。不幸的是,您在 firebase 中错误地使用了 headers
字段。 apns
object 应如下所示,其中 headers 在 apns
内,因为它们是 apns
特定的 headers:
apns: {
headers: {
"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
},
payload: {
aps: {
category: "NEW_MESSAGE_CATEGORY",
contentAvailable: true,
}
}
}
如果消息结构错误,您仍然可能在设备上收到错误消息,例如,告诉您优先级为 10(如果未设置,则默认为 10)。因为后台消息(无通知)不会按照文档发送,所以在 Console.app
中你应该看到:
CANCELED: com.apple.pushLaunch.com.example.my-example-app-bundle-id:3D8BB6 at priority 10 <private>!
Additionally, the notification’s POST request should contain the apns-push-type header field with a value of background, and the apns-priority field with a value of 5. - Pushing Background Updates to Your App
- 调试时,如果 iOS 应用程序处于 foreground/background 并且向设备发送了测试通知或有效的 VoIP 呼叫(FCM 数据消息),应用程序永远不会收到或(也不会所需的断点是否命中)。是什么导致 app/iOS 收不到这些 FCM 消息?
这与#2 相同。
呃,尝试在 AppDelegate
中执行此操作:
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// ADD THIS LINE
UNUserNotificationCenter.current().delegate = self
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// ADD THIS FUNCTION
override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Need to do this or app will eat the notification if app is in foreground
completionHandler([.alert, .badge, .sound])
}
编辑:注意到 Ben 的答案仍然适用并且推送在模拟器中不起作用,您必须 运行 在物理设备上。
我正在从事的一个项目需要使用 FCM 推送通知和消息进行 VoIP 控制等。
问题:
我的 iOS 物理设备 收到推送通知,但 仅当应用程序处于后台或关闭时 NOT 在前台时。此外,它 根本不接收任何 fcm 消息 (数据)通知,例如只有数据而没有通知的消息 title/body 即 VoIP 状态消息。
我的设置步骤如下(可以找到我遵循的详细设置和一般流程here):
注册一个(
Info.plist
)(见here)下载并使用Xcode安装
GoogleService-Info.plist
(参见here)创建 APNS 密钥并启用推送通知并将
.p8
文件上传到 Firebase(参见 here)修改为Swift代码:
AppDelegate.swift
代码:
import UIKit
import Flutter
import Firebase // this is added, and (see further down)
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure() //add this before the code below
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
- 将功能添加到iOS
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>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
...
<key>NSCameraUsageDescription</key>
<string>Allows taking a self-portrait profile image or sending a photo while chating</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Allows using your location to make determining country & location picking easier (optional)</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allows using micrphone to chat with someone when making a call</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Allows opening files from gallery</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>bluetooth-central</string>
<string>external-accessory</string>
<string>fetch</string>
<string>location</string>
<string>processing</string>
<string>remote-notification</string>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<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>
注意:显示的背景模式 here 无法启用,而是我使用一组复选框来启用它们以启用 Background Fetch
等
- FCM - 监听并显示和notifications/messages使用一些通知(显示)服务,例如awesome notifications
实施遵循这些 guidelines:
前台消息:
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
...
});
后台消息:
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
问题扩展:
调试时,如果 iOS 应用程序在 foreground/background 中并且测试通知或有效的 VoIP 呼叫(FCM 数据消息)被发送到设备,应用程序永远不会收到或(也不会所需的断点是否命中)。
什么会导致 app/iOS 收不到这些 FCM 消息?
从 Firebase 云功能服务发送的示例 VoIP 消息:
// build notification on liked status
const payload = {
token: fcmToken,
data: {
uid: context.auth.uid,
room_id: roomId,
...
},
android: {
priority: 'high',
ttl: 0,
notification: {
priority: 'max',
defaultSound: true,
defaultVibrateTimings: true,
sticky: true,
clickAction: "FLUTTER_NOTIFICATION_CLICK",
}
},
notification: {
title: `Incoming call from ${name}`,
body: `Accept/Reject call`,
// icon: 'your-icon-url', // Add profile image URL here if necessary
// clickAction: 'FLUTTER_NOTIFICATION_CLICK' // required only for onResume or onLaunch callbacks
},
apns: {
payload: {
aps: {
category: "NEW_MESSAGE_CATEGORY",
contentAvailable: true,
}
}
},
headers: {
"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
},
};
await admin.messaging().send(payload);
原答案(更通用/对更多人有用):
FCM 不能在 iOS 模拟器上运行,所以问题已经解决了一半。对于物理设备,请查看一些 debugging steps 我在我工作的库中详细写的。
推送通知在 iOS 上出错的方式有多种。当您说:the app never receives
时,您的意思是您的 Flutter 应用程序没有收到消息吗?您的设备可能仍在接收它们。
- 在您的 mac 上打开
Console.app
,然后开始记录您的物理设备。过滤dasd
个进程,你可以看到推送通知相关的日志,看看你的设备是否拒绝了推送通知,没有将它发送到你的应用程序。最有可能的是,您的消息结构存在问题。 - 你也可以尝试在Flutterfire firebase_messaging iOS代码内部的iOS端调试
didReceiveRemoteNotification
方法。 那 表示消息已被应用程序接收。
让我具体回答每个问题/疑虑 (3):
- 我的 iOS 物理设备收到推送通知,但只有当应用程序在后台或关闭时才如此但在前台时不会。
所以你想在前台显示通知?您需要实现 userNotificationCenter(_:willPresent:withCompletionHandler:)
,例如 Swift:
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if #available(iOS 14.0, *) {
completionHandler(.banner)
} else {
completionHandler(.alert)
}
}
为了在此处为您提供更多帮助,您需要设置委托。在上面实现的方法中打个断点,看看它是否真的被调用了。
- 在Swift中:
UNUserNotificationCenter.current().delegate = self;
- 在Objective-C中:
UNUserNotificationCenter.currentNotificationCenter.delegate = self;
- 此外,它根本不接收任何 fcm 消息(数据)通知,例如只有数据而没有通知的消息 title/body 即 VoIP 状态消息。
如果不设置通知字段,则需要设置优先级为5,推送类型为background
,contentAvailable
为true
。不幸的是,您在 firebase 中错误地使用了 headers
字段。 apns
object 应如下所示,其中 headers 在 apns
内,因为它们是 apns
特定的 headers:
apns: {
headers: {
"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
},
payload: {
aps: {
category: "NEW_MESSAGE_CATEGORY",
contentAvailable: true,
}
}
}
如果消息结构错误,您仍然可能在设备上收到错误消息,例如,告诉您优先级为 10(如果未设置,则默认为 10)。因为后台消息(无通知)不会按照文档发送,所以在 Console.app
中你应该看到:
CANCELED: com.apple.pushLaunch.com.example.my-example-app-bundle-id:3D8BB6 at priority 10 <private>!
Additionally, the notification’s POST request should contain the apns-push-type header field with a value of background, and the apns-priority field with a value of 5. - Pushing Background Updates to Your App
- 调试时,如果 iOS 应用程序处于 foreground/background 并且向设备发送了测试通知或有效的 VoIP 呼叫(FCM 数据消息),应用程序永远不会收到或(也不会所需的断点是否命中)。是什么导致 app/iOS 收不到这些 FCM 消息?
这与#2 相同。
呃,尝试在 AppDelegate
中执行此操作:
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// ADD THIS LINE
UNUserNotificationCenter.current().delegate = self
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// ADD THIS FUNCTION
override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Need to do this or app will eat the notification if app is in foreground
completionHandler([.alert, .badge, .sound])
}
编辑:注意到 Ben 的答案仍然适用并且推送在模拟器中不起作用,您必须 运行 在物理设备上。