Las notificaciones de fondo de FCM no funcionan en iOS
Tengo un problema con la notificación FCM en iOS.
Recibo notificaciones con éxito cuando mi aplicación está en primer plano (la devolución de llamadadidReceiveRemoteNotification
enappdelegate
se activa), pero no recibo notificaciones cuando la aplicación está en segundo plano (no veo nada en la bandeja de notificaciones de iOS).
Entonces, creo que el problema está en el formato del mensaje enviado por FCM. El json enviado por mi servidor a FCM tiene el siguiente formato:
{
"data":{
"title":"mytitle",
"body":"mybody",
"url":"myurl"
},
"notification":{
"title":"mytitle",
"body":"mybody"
},
"to":"/topics/topic"
}
Como puede ver, hay dos bloques en mi json: un bloque de notificaciones (para recibir notificaciones en segundo plano) y un bloque de datos (para recibir notificaciones en primer plano).
No puedo entender por qué no se reciben notificaciones en segundo plano. Mis dudas son sobre el orden de los bloques (¿es un problema si pongo el bloque de "datos" antes del bloque de "notificación"?).
EDITAR: Más información sobre el problema.
Este es mi 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:.
}
}
La única forma en que puedo recibir mensajes en primer plano es deshabilitando el método swizzling, configurando FirebaseAppDelegateProxyEnabled en NO en mi info.plist.
En este caso, la documentación de FCM dice que tengo que implementar en mi appdelegate.swift dos métodos:
- FIRMessaging.messaging().appDidReceiveMessage(userInfo) in didReceiveRemoteNotification callback
- FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox) in didRegisterForRemoteNotificationsWithDeviceToken callback
Pero si implemento esas funciones, los mensajes dejan de llegar incluso cuando la aplicación está en primer plano.
Sé que esto es muy extraño.
EDITAR 2:
Cuando la aplicación está en segundo plano, no se recibe la notificación, pero cuando abro mi aplicación, se recibe la misma notificación de inmediato (se activa el método didReceiveRemoteNotification).