I am attempting to send a simple push notification from the firebase notification console to a specific device using an FCM token. The firebase notification console shows the notification as sent but the device does not receive it. I have tried sending the notification and then waiting to see if the console logs from didReceiveRemoteNotification, but the notification takes too long (hours) to be shown as sent in the firebase console (even when I set the priority to high).
App Delegate
import UIKit
import Firebase
import FirebaseStorage
import FirebaseDatabase
import FirebaseMessaging
import CoreData
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Use Firebase library to configure APIs
FirebaseApp.configure()
/////
// For Firebase Cloud Messaging (FCM)
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
// End of [for Firebase Cloud Messaging (FCM)]
/////
return true
}
///////////////////////
// FCM Setup
// Monitor token generation for FCM: Be notified whenever the FCM token is updated
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
}
// Monitor token generation for FCM:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
} // Handle messages received through the FCM APNs interface
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
print("didReceiveRemoteNotification")
// 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
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
// gcm_message_id
if let messageID = userInfo["gcmMessageIDKey"] {
print("Message ID: \(messageID)")
}
^My guess is that the issue may have to do with the "gcm_message_id"/"gcmMessageId"/"gcm.message_id" as it is different in each of the three approaches I tried below
// Print full message.
print(userInfo)
}
// Handle messages received through the FCM APNs interface
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("didReceiveRemoteNotification (withCompletionHandeler)")
// 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
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo["gcmMessageIDKey"] {
print("Message ID: \(messageID)")
}
^My guess is that the issue may have to do with the "gcm_message_id"/"gcmMessageId"/"gcm.message_id" as it is different in each of the three approaches I tried below
// Print full message.
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
// End of [FCM Setup]
///////////////////////
}
View Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Retrieve the current registration token for Firebase Cloud Messaging (FCM)
let token = Messaging.messaging().fcmToken
print("FCM token: \(token ?? "")")
}
}
Entitlements & Enabling Push Notifications
I have added the push entitlements and enabled background modes for push notifications and added the GoogleService-Info.plist to my project.
Method for sending Notification
I am creating notification from the Firebase Notifications console (as shown below) so there should be no issue with the structure of the notification itself.
I have tried the following approaches to remedy the issue, but all have produced the same result:
Google Firebase Documentation
Remote Notifications w/ firebase tutorial
Google Firebase Quickstart
Does anyone know why the notification is being marked as sent in the firebase notification console but not showing up on the device?
A couple of troubleshooting steps I use when working with push notifications are:
Get push working independent of the firebase services first. I use this tool.
Make sure that you dont have any bundle identifier namespace collisions. So for example having any combination of appstore build, testflight build, and / or develop build of an app on the device. Delete all but one instance of the app. The bundle identifier is how your device knows which app to route the push notification to.
When all else fails - I try to isolate the issue by building a new sample project and hook it up to a new firebase project and see if I can narrow my focus down to just being able to get push working without any other business logic in my app. This helps me prove to my self that I haven't gone insane, and proves to me that it's not some mysterious network condition leading to my woes.
I hope this helps you as you work get it all figured out.
Try to add the following code to didRegisterForRemoteNotificationsWithDeviceToken func:
Messaging.messaging().apnsToken = deviceToken
So it will look like this:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
It is work for me.
Does anyone know why the notification is being marked as sent in the firebase notification console but not showing up on the device?
Because "sent" does not mean "received".
Notifications cannot be guaranteed to be received on the device. With basic APNS infrastructure you even cannot get the information if a notifications was received or processed on the device.
If you don't receive a successfully sent message on the device there can be many reasons. Furthermore, even if you receive a Firebase token, that does not mean that your device can receive the notification in any case.
To isolate the problem I would suggest to build up the minimal setup and use APNS without Firebase. You could use Terminal or NWPusher (https://github.com/noodlewerk/NWPusher) for sending notifications from your local macOS system and the iOS native remote push notifications framework for receiving notifications.
Keep care to convert the APNS device token to the correct format required for submitting a notification:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.hexEncodedString()
print("Token: \(token)")
}
Data extension:
extension Data {
func hexEncodedString() -> String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
I see that you have done everything in your project's Capabilities.
Some points:
Conform your class to messaging delegate like so:
Messaging.messaging().delegate = self
Make sure you've setup your certificates properly and uploaded everything on Firebase Push Notification Configurations (not exact term).
I've done several applications that uses Firebase Push Notification Service and making a sample app from scratch might help you figure out what you've been doing wrong.
And... here's a nicer code block for registering your application for push notification.
// Setup Push Notifications
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
if error == nil{
DispatchQueue.main.async(execute: {
application.registerForRemoteNotifications()
})
}
}
}
else {
let notificationTypes: UIUserNotificationType = [.sound, .alert, .badge]
let notificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerForRemoteNotifications()
application.registerUserNotificationSettings(notificationSettings)
}
Related
I have spent about 2 days going through firebases documentation, asking questions and looking at others and following youtube/medium tutorials and more. Here is a github project with my full app delegate, so that all my code is visible.
But I am yet to figure out how to be able to send a notification message from firebase cloud messaging. I have setup Apple push services have gotten and inputed auth key into firebase and written code in my app delegate seen here
Can someone let me know what I need to do to make it work? What have I missed?
Here is my didRegisterForRemoteNotificationsWithDeviceToken method:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
Update:
I have followed again this whole documentation by firebase and still sending notifications from fire cloud messaging does not work.
Clearly however the code is running given that 2 lines I have which save the token to firebase successfully save the token.
I have had a problem like this in the past, what fixed it for me was checking the bundle ID is the same on both the auth key and fire project as well as in Xcode.
Also to make sure your app delegate code is correct just use Firebase's example:
import UIKit
import UserNotifications
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let gcmMessageIDKey = "gcm.message_id"
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
// [START set_messaging_delegate]
Messaging.messaging().delegate = self
// [END set_messaging_delegate]
// Register for remote notifications. This shows a permission dialog on first run, to
// show the dialog at a more appropriate time move this registration accordingly.
// [START register_for_notifications]
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
// [END register_for_notifications]
return true
}
// [START receive_message]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// 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
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (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
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
// [END receive_message]
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
// This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
// If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
// the FCM registration token.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("APNs token retrieved: \(deviceToken)")
// With swizzling disabled you must set the APNs token here.
// Messaging.messaging().apnsToken = deviceToken
}
}
// [START ios_10_message_handling]
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
completionHandler()
}
}
// [END ios_10_message_handling]
extension AppDelegate : MessagingDelegate {
// [START refresh_token]
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
// [END refresh_token]
// [START ios_10_data_message]
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
// To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Received data message: \(remoteMessage.appData)")
}
// [END ios_10_data_message]
}
Assuming you have the APNS Auth Key correctly configured in the firebase console, and considering your appDelegate implementation you have provided, the only piece of code i see missing is the FIRInstanceId creation.
According to Firebase documentation here :
Instance ID provides a unique identifier for each app instance and a mechanism >to authenticate and authorize actions (for example, sending an FCM message).
Once an InstanceID is generated, the library periodically sends information >about the application and the device where it’s running to the Firebase >backend.
And here
To send or receive messages, the app must get a registration token from >FIRInstanceID. This token authorizes an app server to send messages to an app >instance.
Just before where you register for remote notification in your application didFinishLaunching..., add this before the application.registerForRemoteNotifications()
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instange ID: \(error)")
} else if let result = result {
print("Remote instance ID token: \(result.token)")
}
}
Let me know if this helps :)
I am trying to implement push notification using Firebase Cloud Messaging, I send the message through firebase console. when composing a message in Firebase console, I set the badge number to be 1 like the picture below
after that, my app icon in the home screen will always have badge with number "1", even tough I have tried to uninstall and reinstall it, the badge with number "1" is still there.
but it only happens in my iPhone,if I Install it on the other phone, the badge will not show up
I use this code in App delegate to trigger push notification
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
var fcmTokenUser : String?
var firsTimeUsingApp = true
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
print(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! as String)
// To get FCM token that will be sent to APNS via Google FCM
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
Messaging.messaging().delegate = self
let token = Messaging.messaging().fcmToken
fcmTokenUser = token
checkFirstTimeUsingAppOrNot()
moveToNextPage()
// to make status bar in the light mode (in info.plist it also has to be set 'View controller-based status bar appearance' to NO)
UIApplication.shared.statusBarStyle = .lightContent
return true
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String){
// This callback is fired at each app startup (when the user install the app for the very first time) and whenever a new token is generated due to The app is restored on a new device, The user uninstalls/reinstall the app, The user clears app data.
// after fcm generated for the very first time,then fcm can also be retrieved in the 'didFinishLaunchingWithOptions' method above (let token = Messaging.messaging().fcmToken)
fcmTokenUser = fcmToken
moveToNextPage()
}
private func application(application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
Messaging.messaging().apnsToken = deviceToken as Data
}
}
how to resolve this issue?
iOS will remember your applications badge count always, even if your uninstall your app and install it again. For removing your badge you have to do any one of following things,
Send another push notification to your app with badge = 0.
Remove badge count by your own whenever user open your app by using below code, UIApplication.shared.applicationIconBadgeNumber = 0 . Add this line of code in Appdelegate's didBecomeActive(:) method.
Thanks.
This is my first time working on with firebase due to a requirement by the backend so let me know if you don't understand the question or if it doesn't make any sense. What I want to achieve is that I need a Registration_ID which is the FCM Token when user register. So this is how I'm implementing it in AppDelegate.swift > didFinishLaunchingWithOptions
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
After that in AppDelegate.swift I've added these three functions:
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken as Data
}
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
}
func application(application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
Messaging.messaging().apnsToken = deviceToken as Data
}
No the issue is that when it ask for permission of notification and user deny it then how I'll get FCM that I'll send to the backend when user register or login.
An FCM token is generated regardless of the user's response to the authorization request - if the response takes long enough, the FCM token will be generated before the response is given.
Notifications will not work until the user gives permission but generating the FCM token is not dependent on getting permission.
Please note: I have not found this documented anywhere, but through my own testing. I have tested on iOS 10.3.1 and 11.0.
The first time the token is generated the Messaging delegate's didRefreshRegistrationToken is called, I suspect the problem is that you have not set the delegate with
Messaging.messaging().delegate = self
If user not granting permission, you will not able to send push notification. 3rd party you use cant change this permission (amazon, firebase). For Firebase cloud Messaging Push notification permission is a must.
I updated my pods today to the new Firebase 4.0. I went through the suggested changes and grabbed code from the Github example. I will be honest I am at a loss, I take the FCM Token and send a message from the firebase console and I get nothing.
I refresh and it says the message was sent but I check the console and the device and nothing is there. What am I missing?
Here is my appdelegate:
//
// Created by Erik Grosskurth on 4/24/17.
//
import UIKit
import FirebaseAnalytics
import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications
import SystemConfiguration
import MobileCoreServices
import Quickblox
import QuickbloxWebRTC
let kQBApplicationID:UInt = 3545252534
let kQBAuthKey = "udfgsegsetrgsextr"
let kQBAuthSecret = "setbsetbsetbsetrbset"
let kQBAccountKey = "sbrtsbrtbsrtbsrtbrt"
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let gcmMessageIDKey = "gcm.message_id"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// [START register_for_notifications]
FirebaseApp.configure()
Messaging.messaging().delegate = self
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
if (Messaging.messaging().fcmToken != nil) {
DataModel.sharedInstance.sessionInfo.FirebaseAccessToken = Messaging.messaging().fcmToken!
print("FCM token: \(DataModel.sharedInstance.sessionInfo.FirebaseAccessToken)")
}else {
print("token was nil")
}
// [END register_for_notifications]
//Quickblox config
QBSettings.setApplicationID(kQBApplicationID)
QBSettings.setAuthKey(kQBAuthKey)
QBSettings.setAuthSecret(kQBAuthSecret)
QBSettings.setAccountKey(kQBAccountKey)
// Set settings for zone
QBSettings.setApiEndpoint("https://api.quickblox.com", chatEndpoint: "chat.quickblox.com", forServiceZone: .production)
// Activate zone
QBSettings.setServiceZone(.production)
QBSettings.setKeepAliveInterval(30)
QBSettings.setAutoReconnectEnabled(true)
QBRTCConfig.setStatsReportTimeInterval(1)
QBRTCConfig.setDialingTimeInterval(5)
QBRTCConfig.setAnswerTimeInterval(60)
return true
}
// [START receive_message]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
Messaging.messaging().appDidReceiveMessage(userInfo)
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
Messaging.messaging().appDidReceiveMessage(userInfo)
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
// when APNs has assigned the device a unique token
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("didRegisterForRemoteNotificationsWithDeviceToken()")
Messaging.messaging().apnsToken = deviceToken
//Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.sandbox)
//Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.prod)
//Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.unknown)
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
DataModel.sharedInstance.sessionInfo.APNSAccessToken = deviceTokenString
print("APNS Access Token: \(deviceTokenString)")
}
// [END receive_message]
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
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 invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
QBChat.instance().disconnect { (error) in
if error != nil {
print("error: \(String(describing: error))")
} else {
print("success for applicationDidEnterBackground")
}
}
}
func applicationWillEnterForeground(_ application: UIApplication) {
let qbUser = QBUUser()
qbUser.id = DataModel.sharedInstance.qbLoginParams.id
qbUser.password = DataModel.sharedInstance.sessionInfo.QBPassword
QBChat.instance().connect(with: qbUser) { (error) in
if error != nil {
print("error: \(String(describing: error))")
} else {
print("success for applicationWillEnterForeground")
}
}
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
QBChat.instance().disconnect { (error) in
if error != nil {
print("error: \(String(describing: error))")
} else {
print("success for applicationWillTerminate")
}
}
}
// LOCK IN PORTRAIT MODE
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask(rawValue: UIInterfaceOrientationMask.portrait.rawValue)
}
}
// [START ios_10_message_handling]
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// With swizzling disabled you must let Messaging know about the message, for Analytics
Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
completionHandler()
}
}
// [END ios_10_message_handling]
extension AppDelegate : MessagingDelegate {
// [START refresh_token]
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
DataModel.sharedInstance.sessionInfo.FirebaseAccessToken = fcmToken
print("Firebase registration token: \(fcmToken)")
}
// [END refresh_token]
// [START ios_10_data_message]
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
// To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Received data message: \(remoteMessage.appData)")
}
// [END ios_10_data_message]
}
Here are the logs:
2017-05-31 16:49:56.937067-0400 Telemed[1843:937699] [Firebase/Analytics][I-ACS003016] Firebase Analytics App Delegate Proxy is disabled. To log deep link campaigns manually, call the methods in FIRAnalytics+AppDelegate.h.
2017-05-31 16:49:56.937 Telemed[1843] <Warning> [Firebase/Analytics][I-ACS003016] Firebase Analytics App Delegate Proxy is disabled. To log deep link campaigns manually, call the methods in FIRAnalytics+AppDelegate.h.
2017-05-31 16:49:57.023568-0400 Telemed[1843:937706] [Firebase/Analytics][I-ACS005000] The AdSupport Framework is not currently linked. Some features will not function properly. Learn more at
2017-05-31 16:49:57.023 Telemed[1843] <Warning> [Firebase/Analytics][I-ACS005000] The AdSupport Framework is not currently linked. Some features will not function properly. Learn more at
2017-05-31 16:49:57.028454-0400 Telemed[1843:937697] [Firebase/Analytics][I-ACS023007] Firebase Analytics v.4000000 started
2017-05-31 16:49:57.028 Telemed[1843] <Notice> [Firebase/Analytics][I-ACS023007] Firebase Analytics v.4000000 started
2017-05-31 16:49:57.029042-0400 Telemed[1843:937697] [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see )
2017-05-31 16:49:57.029 Telemed[1843] <Notice> [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see )
FCM token: myTokenIsHere
2017-05-31 16:49:57.101756-0400 Telemed[1843:937699] [Firebase/Analytics][I-ACS032003] iAd framework is not linked. Search Ad Attribution Reporter is disabled.
2017-05-31 16:49:57.102 Telemed[1843] <Warning> [Firebase/Analytics][I-ACS032003] iAd framework is not linked. Search Ad Attribution Reporter is disabled.
2017-05-31 16:49:57.103700-0400 Telemed[1843:937697] [Firebase/Analytics][I-ACS023012] Firebase Analytics enabled
2017-05-31 16:49:57.103 Telemed[1843] <Notice> [Firebase/Analytics][I-ACS023012] Firebase Analytics enabled
Destroyed Session but Saved Login
2017-05-31 16:49:57.158678-0400 Telemed[1843:937637] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-05-31 16:49:57.159102-0400 Telemed[1843:937637] [MC] Reading from public effective user settings.
didRegisterForRemoteNotificationsWithDeviceToken()
APNS Access Token: 091F01B...
>>>------> **SOLVED <-------<<<**
FOR A DEEP DIVE INTO THE TROUBLESHOOTING AND THE ULTIMATE FACEPALM AT THE END... FOLLOW THIS THREAD:
https://github.com/firebase/quickstart-ios/issues/290
SPECIAL THANKS TO THE RIZ FOR HIS TIME IN FIGURING OUT THE ISSUE!!
Firebase Cloud Messaging is quite complex to get set up correctly on iOS, because you have to ensure that your APNs configuration is working, and then add FCM on top of that.
APNs Setup
APNs Authentication Keys are also really nice by apple because they don't expire, works in both sandbox and production environments, and the same key (as a .p8 file) can be used to send push notifications to all apps under that Apple Developer account.
It also introduces a new area for a potential failure: typos. You can type in a random bundle id, and as long as the right "team" is configured in Xcode, your app will happily get an APNs device token.
Sending a notification to that device token via APNs (I used this script to send test notifications over HTTP/2 + Auth Keys) will yield a DeviceTokenNotForTopic error, so that should help you figure out what went wrong.
Checklist
Ensure your app bundle id matches the bundle id you're sending to
Ensure you have an entitlements file, with at least an aps-environment key set to development (this is automatically updated for you in release builds, by Xcode)
If using "automatic" configuration (i.e. swizzling) in other SDKs like Firebase, make sure you're getting an APNs token in application:didRegisterForRemoteNotificationsWithDeviceToken:. That at least confirms that your APNs set up is okay (but still you might have a typo)
FCM Setup
Be sure that the GoogleService-Info.plist file you're using is for the same app as the BUNDLE_ID key in the plist. I've seen developers think that because APNs Keys should work just fine across all of their apps, that the same GoogleService-Info.plist is fine across apps, but that's not the case.
Testing
I'd also recommend sending yourself test notifications using FCM's HTTP API from Terminal, to help debug the issue. Unlike the Firebase Notifications Console, it will give you any raw errors it encounters. Here's an example:
curl -X "POST" "https://fcm.googleapis.com/fcm/send" \
-H "Authorization: key=SERVER_KEY" \
-H "Content-Type: application/json" \
-d $'{
"notification": {
"body": "Testing with direct FCM API",
"title": "Test Message",
"badge": "0",
"sound": "default"
},
"registration_ids": [
"FCM_TOKEN"
]
}'
Fill in your SERVER_KEY with the value in your Firebase Console > Project Settings > Cloud Messaging > Server Key. Replace FCM_TOKEN with your FCM token.
I use Paw to send test notifications to my apps when I'm working on the FCM SDK.
I was using .p8 file for sending notification, though notification wasn't coming; then I realised the Apple Push Notifications service (APNs) wasn't enabled under .p8 file.
After enabling it, all seems to work now.
import Firebase
import FirebaseInstanceID
import FirebaseMessaging
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
registerForPushNotifications(application)
FIRApp.configure()
// Add observer for InstanceID token refresh callback.
NSNotificationCenter
.defaultCenter()
.addObserver(self, selector: #selector(AppDelegate.tokenRefreshNotificaiton),
name: kFIRInstanceIDTokenRefreshNotification, object: nil)
// Override point for customization after application launch.
return true
}
func registerForPushNotifications(application: UIApplication) {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("===== didReceiveRemoteNotification ===== %#", userInfo)
}
func tokenRefreshNotificaiton(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.
connectToFcm()
}
func connectToFcm() {
FIRMessaging.messaging().connectWithCompletion { (error) in
if (error != nil) {
print("Unable to connect with FCM. \(error)")
} else {
print("Connected to FCM.")
}
}
}
Also to done in Info.plist FirebaseAppDelegateProxyEnabled = NO
I don't know for now but I got the print(...) in didReceiveRemoteNotification but don't get the popup. I send the message from Firebase -> Console -> Notification -> Single device and copy here the token which I got from xCode Console -> func tokenRefreshNotificaiton
Get the next in console, but don't get popup
<FIRAnalytics/INFO> Firebase Analytics enabled
InstanceID token: TOKEN_ID
Connected to FCM.
===== didReceiveRemoteNotification ===== %# [notification: {
body = test;
e = 1;
}, collapse_key: com.pf.app, from: 178653764278]
Also app configurations
set the following code in AppDelegate.m
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// for development
[[FIRInstanceID instanceID] setAPNSToken:deviceToken type:FIRInstanceIDAPNSTokenTypeSandbox];
// for production
// [[FIRInstanceID instanceID] setAPNSToken:deviceToken type:FIRInstanceIDAPNSTokenTypeProd];
}
I'm guessing your app is in the foreground when testing. When your app is in the foreground no visible notification is triggered, instead you receive the callback to didReceiveRemoteNotification. See the documentation for more info.
To verify, put your app in the background and try sending the push notification again.
I have same configuration you have and it works like AdamK said. (While in background mode, notification appears.) Also check your certificates.
First check with Firebase Notification Console to see if the notification is sending or not. If it is success, then the problem is in the code side; otherwise, check what error is coming in Firebase. If you receive error message as APNs missing, you need to check with development/production .p12 file in Project Setting->Cloud Messaging tab.
Just use this function in your app delegate sandbox for development prod for prodction
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.sandbox)
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.prod)
}
Are you using https://pushtry.com for test the FCM notification? then don't use because I have lots of issue with this website for testing notification some time it working and some times not. it's not giving consistence result and it may be effect in FCM flow and totally block the receiving notifications.
I recommended to use https://fcm.snayak.dev for test the notification.