As notificações em segundo plano do FCM não funcionam no iOS
Estou com um problema com a notificação do FCM no iOS.
Recebo notificações com êxito quando meu aplicativo está em primeiro plano (o retorno de chamadadidReceiveRemoteNotification
noappdelegate
é acionado), mas não recebo notificações quando o aplicativo está em segundo plano (não vejo nada na bandeja de notificações do iOS).
Então, acho que o problema está no formato da mensagem enviada pelo FCM. O json enviado pelo meu servidor para o FCM, está no seguinte formato:
{
"data":{
"title":"mytitle",
"body":"mybody",
"url":"myurl"
},
"notification":{
"title":"mytitle",
"body":"mybody"
},
"to":"/topics/topic"
}
Como você pode ver, existem dois blocos no meu json: um bloco de notificação (para receber notificações em segundo plano) e um bloco de dados (para receber notificações em primeiro plano).
Não consigo entender por que as notificações em segundo plano não são recebidas. Minhas dúvidas são sobre a ordem dos blocos (é um problema se eu colocar o bloco "dados" antes do bloco "notificação"?).
EDITAR: Mais informações sobre o problema.
Este é o meu 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:.
}
}
A única maneira de receber mensagens em primeiro plano é desativando o método swizzling, configurando FirebaseAppDelegateProxyEnabled como NO no meu info.plist.
Nesse caso, a documentação do FCM diz que eu tenho que implementar no meu appdelegate.swift dois métodos:
- FIRMessaging.messaging().appDidReceiveMessage(userInfo) in didReceiveRemoteNotification callback
- FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox) in didRegisterForRemoteNotificationsWithDeviceToken callback
Mas se eu implementar essas funções, as mensagens param para chegar mesmo quando o aplicativo está em primeiro plano.
Eu sei que isso é muito estranho.
EDIT 2:
Quando o aplicativo está em segundo plano, a notificação não é recebida, mas quando abro o aplicativo, a mesma notificação é recebida imediatamente (o método didReceiveRemoteNotification é acionado).