FCM background notifications ne fonctionne pas dans iOS
j'ai un problème avec la notification FCM sur iOS.
je reçois des notifications avec succès lorsque mon application est en avant-plan (le rappel didReceiveRemoteNotification
dans appdelegate
est déclenché), mais je ne reçois pas de notifications lorsque l'application est en arrière-plan (Je ne vois rien dans le plateau de notification de iOS).
donc, je pense que le problème est dans le format du message envoyé par FCM. Le json envoyé par mon serveur à la FCM, est dans le format suivant::
{
"data":{
"title":"mytitle",
"body":"mybody",
"url":"myurl"
},
"notification":{
"title":"mytitle",
"body":"mybody"
},
"to":"/topics/topic"
}
comme vous pouvez le voir, il y a deux blocs dans mon json: un bloc de notification (pour recevoir des notifications en arrière-plan), et un bloc de données (pour recevoir des notifications en avant-plan).
Je ne comprends pas pourquoi les notifications en arrière-plan ne sont pas reçues. Mes doutes portent sur l'ordre des blocs (est-ce un problème si je mets le bloc "données" avant le bloc "notification"?).
EDIT: Plus des infos sur le problème.
c'est mon appdelegate.swift:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
var window: UIWindow?
// Application started
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
{
let pushNotificationSettings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
FIRApp.configure()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "tokenRefreshNotification:", name: kFIRInstanceIDTokenRefreshNotification, object: nil)
return true
}
// Handle refresh notification token
func tokenRefreshNotification(notification: NSNotification) {
let refreshedToken = FIRInstanceID.instanceID().token()
print("InstanceID token: (refreshedToken)")
// Connect to FCM since connection may have failed when attempted before having a token.
if (refreshedToken != nil)
{
connectToFcm()
FIRMessaging.messaging().subscribeToTopic("/topics/topic")
}
}
// Connect to FCM
func connectToFcm() {
FIRMessaging.messaging().connectWithCompletion { (error) in
if (error != nil) {
print("Unable to connect with FCM. (error)")
} else {
print("Connected to FCM.")
}
}
}
// Handle notification when the application is in foreground
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// Print message ID.
print("Message ID: (userInfo["gcm.message_id"])")
// Print full message.
print("%@", userInfo)
}
// Application will enter in background
func applicationWillResignActive(application: UIApplication)
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
// Application entered in background
func applicationDidEnterBackground(application: UIApplication)
{
FIRMessaging.messaging().disconnect()
print("Disconnected from FCM.")
}
// Application will enter in foreground
func applicationWillEnterForeground(application: UIApplication)
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
// Application entered in foreground
func applicationDidBecomeActive(application: UIApplication)
{
connectToFcm()
application.applicationIconBadgeNumber = 0;
}
// Application will terminate
func applicationWillTerminate(application: UIApplication)
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
le seul moyen que je puisse recevoir des messages en premier plan, est en désactivant la méthode swizzling, mettant FirebaseAppDelegateProxyEnabled à NO dans mes informations.plist.
dans ce cas, la documentation de la FCM dit que je dois implémenter dans mon appdelegate.swift deux méthodes:
- FIRMessaging.messaging().appDidReceiveMessage(userInfo) in didReceiveRemoteNotification callback
- FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox) in didRegisterForRemoteNotificationsWithDeviceToken callback
mais si je implémente ces fonctions, les messages s'arrêtent à arriver, même lorsque l'application est en arrière plan.
je sais que c'est très étrange.
EDIT 2:
Lorsque l'application est en arrière-plan la notification n'est pas reçue, mais quand j'ouvre mon application, la même notification est reçue immédiatement (méthode didReceiveRemoteNotification est tiré).
6 réponses
en supposant que vous avez tout mis en place correctement, alors le réglage du priority
du message de normal
à high
devrait le faire apparaître immédiatement. Cela est dû à la façon dont iOS regroupe les notifications et les traite. Vous pouvez lire à propos de la priorité des notifications FCM ici . S'il vous plaît noter que vous ne devriez pas vraiment utiliser high
dans la production à moins qu'il n'y ait un bon cas pour elle, car il a une pénalité de la batterie.
Voici la référence de Apple docs
La priorité de la notification. Spécifiez l'une des valeurs suivantes:
10-envoyer le message de pression immédiatement. Notifications avec cette priorité doit déclencher une alerte sonore ou d'un insigne sur le périphérique cible. C'est un erreur d'utiliser cette priorité pour une notification push qui ne contient que la clé du contenu-disponible.
5-Envoyer la poussée message à un moment qui tient compte de la puissance considérations pour l'appareil. Les Notifications avec cette priorité pourraient être groupés et livrés en rafales. Elles sont limitées, et dans certains les caisses ne sont pas livrées. Si vous omettez cet en-tête, le serveur APNs la priorité à 10.
vous devez définir la propriété content_available
à true like so:
{
"data":{
"title":"mytitle",
"body":"mybody",
"url":"myurl"
},
"notification":{
"title":"mytitle",
"body":"mybody",
"content_available": true
},
"to":"/topics/topic"
}
il y a une case bleue dans cette section qui dit ceci: https://firebase.google.com/docs/cloud-messaging/concept-options#notifications
vous pourriez avoir besoin d'ajouter le droit à Notification push. Pour ce faire, allez dans les paramètres de votre cible, puis cliquez sur "Capabilities" et activez "Push Notifications".
la priorité et le contenu disponible (tel que mentionné dans d'autres réponses) sont les éléments clés pour s'assurer que vous recevez les notifications. Les Tests ont montré des résultats intéressants, alors j'ai pensé les partager ici.
résultats des tests: Swift 3, Xcode 8, iOS 10
Priority = "haut" => "immédiate" (à moins évident retards de réseau) de réception de message.
priorité = "normal" = > résultats Divers (généralement rapides, mais manifestement plus lents) que "élevé")
content_av available = true in the notifications (no payload message)
- premier plan = données reçues comme prévu
- Background = données reçu comme prévu (lors de l'ouverture de l'application)
content_av available = true in the top level (no payload message)
- premier plan = données reçues comme prévu
- Fond = données reçu comme prévu (lors de l'ouverture de l'application)
content_av available = true in the notifications (with message {title/body})
- premier plan = données reçues deux fois
- Background = données reçu à deux reprises (lors de l'ouverture de l'application)
content_av available = true in the top level (with payload message)
- premier plan = données reçues deux fois
- Background = données reçu à deux reprises (lors de l'ouverture de l'application)
CONCLUSIONS:
- alors que la priorité est une cause possible de ne pas recevoir de messages, le facteur le plus IMPORTANT est que vous devez avoir soit "content_avitable" ou un message de charge utile.
- content_available DOIT être utilisé sur des données uniquement à des charges (sans cela, aucun message n'est jamais envoyé).
- content_aviable ne doit pas être utilisé sur les charges utiles qui contiennent des messages, car il provoque l'envoi de messages doubles à partir de la FCM.
- aucune différence dans l'utilisation du contenu_ disponible au niveau supérieur ou dans les notifications.
modifier: résultats D'essais supplémentaires: - si vous avez un titre msg vous devez avoir un corps msg ou vous ne recevez pas d'alerte.
la partie étrange de ceci est que vous obtiendrez le vibromasseur, badge et le son, mais la boîte d'alerte n'apparaîtra pas à moins d'avoir un corps aussi bien que le titre.
- pour FCM lorsque la demande est en arrière-plan ou en premier plan et OS <10 application ( _ : didReceiveRemoteNotification:) la méthode se déclenche.
- lorsque l'application est de premier plan et OS = > 10 userNotificationCenter:willPresentNotification:withCompletionHandler: méthode d'incendie.
-lors de l'envoi d'un message de données sans composante de notification: application ( _ : didReceiveRemoteNotification:) la méthode se déclenche.
- lors de l'envoi message de données avec composante de notification : userNotificationCenter:willPresentNotification:withCompletionHandler: méthode d'incendie.
j'ai eu ce problème, avec la propriété content_available
. La solution était de supprimer et de réinstaller l'application à partir du dispositif iOS.