I'm getting strange behavior in iOS 14 with regards to remote notifications on iOS and Firebase Cloud Messaging (FCM).
DEVICE
First, I cannot send Test notifications to my device from the FCM console. I followed the documentation verbatim, and a few tutorials for good measure. Here's my AppDelegate:
import SwiftUI
import Firebase
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
FirebaseConfiguration.shared.setLoggerLevel(.min)
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
(granted, error) in
print("Permission granted: \(granted)")
// 1. Check if permission granted
guard granted else {
print("Permission not granted")
return
}
// 2. Attempt registration for remote notifications on the main thread
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
application.registerForRemoteNotifications()
return true
}
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)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
}
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
Messaging.messaging().appDidReceiveMessage(userInfo)
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
print(userInfo)
completionHandler([[.banner, .sound]])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
Messaging.messaging().appDidReceiveMessage(userInfo)
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
print(userInfo)
completionHandler()
}
}
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("Firebase registration token: \(fcmToken ?? "")")
let dataDict:[String: String] = ["token": fcmToken ?? ""]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "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.
}
func application(application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
}
let gcmMessageIDKey = "gcm_Message_ID_Key"
Other things I've tried:
Disabling method swizzling in the Info.pList (FirebaseAppDelegateProxyEnabled - Boolean - 0)
Adding Messaging.messaging().appDidReceiveMessage(userInfo) to update the appropriate methods after disabling swizzling in the App Delegate.
Sending App to background (can we test in foreground while app is running? Will it work in foreground if it works at all?)
SIMULATOR
Second, I am unable to send notifications to the simulator, unless I drag the aps file from Finder to said simulator. This aps file is a JSON file I can use for testing push notifications on simulators. Note: "alert" key has been deprecated in iOS 14.
{
"Simulator Target Bundle": "com.Example.app",
"aps": {
"sound": "default",
"badge": 1
}
}
The terminal command utility looks like this
xcrun simctl push <device> com.Example example.apns
where the <device> is the simulator device identifier (you can also use "booted" in lieu of the device identifier if only one simulator is open) and example.apns is your file. I created my apns file in Atom.
This is what I get in the logs:
These are my reports from the FCM console:
SOLVED
My implementation must've been wrong. I followed this verbatim and got it working on simulator and device. The simulator read, as a default error, remote notifications are not supported in the simulator.
There are a couple reasons why this might be the case:
Bundle IDs from Pusher to Project don't match
Not using the whole identifier e.g. 11FG89IK4-SHEN-13H4-AJD9-SN39FNS82JR for the simulator.
I also had an error in terminal Invalid device: [identifier] when I tried on a real device. The reason is I was using the device identifier in Windows > Devices and Simulator for my iPhone instead of the device token I get back in the console from this method:
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
I also used Push Notifications Tester instead of FCM. I don't know what I was doing wrong. I'm certain the Firebase tool works fine. Reasonably something on my end.
Related
I have developed an iOS app, in swift Xcode 11.5, and it is working fine. But when I tried to configure Firebase Cloud Messaging push Notifications, it didn't work!
I've followed the instruction on Firebase console to connect the App, and it was the Firebase account is linked correctly. and this is my AppDelegate.swift:
import UIKit
import Firebase
import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, QBRTCClientDelegate {
let gcmMessageIDKey = "gcmMessageIDKey"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
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
return true
}
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)
}
}
#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([[.alert, .sound]])
}
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()
}
}
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instance ID: \(error)")
} else if let result = result {
print("Remote instance ID token: \(result.token)")
}
}
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.
Messaging.messaging().isAutoInitEnabled = true
}
}
I installed the Firebase/Core, Firebase/Analytics and Firebase/Messaging pods to the project.
I added FirebaseAppDelegateProxyEnabled to the Info.plist and set it to NO.
I've also set up the Production APNS form Apple developer account, and i think it is set correctly.
I've tested sending the Push Notifications locally from the terminal, and it is working.
But the problem is that the notifications are not being received when i do a test notification from Firebase console.
Please advise on how to solve this problem.
I have a problem to solve with a push message. If I send a push message to Firebase,I will receive it well and I will see the message well.
And when I click on the push message, the userNotificationCenter function in the AppDelegate file is executed and the list of actions in the function is executed.
Can I execute a function without receiving a specific message and displaying a message?
Where I'm currently receiving and processing push messages.
#available(iOS 10, *)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let data = response.notification.request.content.userInfo
guard
let push = data[AnyHashable("push")] as? String,
let getdata = data[AnyHashable("getdata")] as? String,
let pushdata = data[AnyHashable("pushdata")] as? String
else {
print("it's not good data")
return
}
print(push)
print(getdata)
print(pushdata)
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler([.alert, .badge, .sound])
}
My purpose is to execute the function without showing the push message to the user when sending a specific message (Ex: push = "noShowPush") from the Firebase.
EDit
I was tried "content_available": true
but get log 6.10.0 - [Firebase/Messaging][I-FCM002019] FIRMessaging received data-message, but FIRMessagingDelegate's-messaging:didReceiveMessage: not implemented
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
// Override point for customization after application launch.
//create the notificationCenter
Messaging.messaging().delegate = self
FirebaseApp.configure()
//Register App For Push Notification
// self.registerAppForPushNotificaition()
let center = UNUserNotificationCenter.current()
let inviteCategory = UNNotificationCategory(identifier: "Notification", actions: [], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
let categories = NSSet(objects: inviteCategory)
center.delegate = self
center.setNotificationCategories(categories as! Set<UNNotificationCategory>)
DispatchQueue.main.async(execute: {
UIApplication.shared.registerForRemoteNotifications()
})
application.registerForRemoteNotifications()
self.updateAppViewUI()
self.window?.makeKeyAndVisible()
return true
}
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) {
Messaging.messaging().appDidReceiveMessage(userInfo)
if Auth.auth().canHandleNotification(userInfo) {
completionHandler(UIBackgroundFetchResult.noData)
return
}
completionHandler(UIBackgroundFetchResult.newData)
}
I have it already Messaging.messaging().delegate = self.
The logs in the Firebase have warned you to set FirebaseAppDelegateProxyEnabled to No, so I added to the Info.list.
and Firebase log changed [Firebase/Core][I-COR000003] The default Firebase app has not yet been configured. Add '[FIRApp configure];' ('FirebaseApp.configure()' in Swift) to your application initialization. Read more:
Are the things I'm revising are going right?
How can I this solution?
I solved the problem. Here's the order in which I've worked out:
Write when you send a push "content_available": true". And don't
include the title and body.
And when I got these error logs, 6.10.0 -
[Firebase/Messaging][I-FCM002019] FIRMessaging received
data-message, but
FIRMessagingDelegate's-messaging:didReceiveMessage: not implemented
The logs in the Firebase have warned you to set
FirebaseAppDelegateProxyEnabled to No, so I added to the
Info.list.
and changed the order of execution of the function.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure() // Let it run first.
Messaging.messaging().delegate = self
self.window = UIWindow(frame: UIScreen.main.bounds)
...
And I installed 'Firebase/Analytics' on the pod.
pod 'Firebase/Analytics'
and inherited the MessagingDelegate and implemented the
Messaging function.
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("fcmToken \(fcmToken)")
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
Log.Info("remort \(remoteMessage.appData)")
let userInfo = remoteMessage.appData
}
This configuration allows you to receive push message data into the messaging function when a push message is sent. This is a push message with a 'contentAvailable' value of True and no title and body.
I have created a sample ios application as per the instructions provided in the Firebase console to receive cloud messages on ios devices. Notification message is sent from the console and gets printed on the app console as well. But the push notification is not received on the device even after giving necessary permission.
I'm using swift 3 and xCode version 10.3. Registered the app in Firebase console and required FCM token is received through the app and added to send a test message to the device. Tried sending lot of messages through the console but none of them are shown on the device. It shows as the messages are being sent successfully on the console.
Following is my AppDelegate.swift file that I have written to receive the push notification.
import UIKit
import UserNotifications
import Firebase
import FirebaseMessaging
import FirebaseInstanceID
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let gcmMessageIDKey = "gcm.message_id"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
FirebaseApp.configure()
Messaging.messaging().delegate = self
Messaging.messaging().shouldEstablishDirectChannel = true
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)
}
// request permission from user to send notification
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { authorized, error in
if authorized {
DispatchQueue.main.async(execute: {
application.registerForRemoteNotifications()
})
}
else{
print("Notification access denied")
}
})
return true
}
// [START receive_message]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
print("APNs token retrieved: \(deviceToken)")
}
}
#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
print(userInfo)
print("Message ID: \(userInfo["gcm.message_id"]!)")
completionHandler(UNNotificationPresentationOptions.alert)
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print("Do what ever you want")
// Print full message.
print("tap on on forground app",userInfo)
completionHandler()
}
}
extension AppDelegate : MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
Messaging.messaging().subscribe(toTopic: "/topics/nutriewell_live")
Messaging.messaging().shouldEstablishDirectChannel = true
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Received data message: \(remoteMessage.appData)")
}
}
I expected a push notification on the ios device but I'm not receiving any.
I'm getting the following output which gets printed with the notification message sent from the console.
2019-08-15 11:20:32.548066+0530 FirebaseIos[800:207059] - <AppMeasurement>[I-ACS036002] Analytics screen reporting is enabled. Call +[FIRAnalytics setScreenName:setScreenClass:] to set the screen name or override the default screen class name.
To disable screen reporting, set the flag FirebaseScreenReportingEnabled to NO (boolean) in the Info.plist
2019-08-15 11:20:32.949393+0530 FirebaseIos[800:207068] 6.2.0 - [Firebase/Messaging][I-FCM001000] FIRMessaging Remote Notifications proxy enabled, will swizzle remote notification receiver handlers. If you'd prefer to manually integrate Firebase Messaging, add "FirebaseAppDelegateProxyEnabled" to your Info.plist, and set it to NO.
Firebase registration token: fsyB51mCvbg:APA91bFvRFPIxauHfKA-v-K29YTWl_TYFTgCpnvODrFA2rG8qN-F8vcLIWcZ-lOChkJw-pIBVhcxg2epBz7AYVALMNLC4Hs6M4ds_pQXytYymjr15KMqOt08_7PkmYkU1jHy6xcw5hvx
2019-08-15 11:20:34.914040+0530 FirebaseIos[800:207059] 6.2.0 - [Firebase/Messaging][I-FCM002024] Format '/topics/nutriewell_live' is deprecated. Only 'nutriewell_live' should be used in subscribeToTopic.
Notification access denied
2019-08-15 11:20:48.869741+0530 FirebaseIos[800:207068] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C7.1:2][0x101537e10] get output frames failed, state 8196
Received data message: [AnyHashable("notification"): {
body = "firebase ios";
e = 1;
tag = "campaign_collapse_key_3627158360856700759";
title = "firebase!";
}, AnyHashable("from"): 591363996390, AnyHashable("collapse_key"): com.combank.Firebase.Ios]
%# [AnyHashable("notification"): {
body = "firebase ios";
e = 1;
tag = "campaign_collapse_key_3627158360856700759";
title = "firebase!";
}, AnyHashable("from"): 591363996390, AnyHashable("collapse_key"): com.combank.Firebase.Ios]
Received data message: [AnyHashable("collapse_key"): com.combank.Firebase.Ios, AnyHashable("from"): 591363996390, AnyHashable("notification"): {
body = "firebase ios";
e = 1;
tag = "campaign_collapse_key_750970355066402639";
title = "firebase!";
}]
%# [AnyHashable("collapse_key"): com.combank.Firebase.Ios, AnyHashable("from"): 591363996390, AnyHashable("notification"): {
body = "firebase ios";
e = 1;
tag = "campaign_collapse_key_750970355066402639";
title = "firebase!";
}]
2019-08-15 11:22:34.475335+0530 FirebaseIos[800:207131] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C1.1:2][0x1015273f0] get output frames failed, state 8196
Any help I can get here for this?
There are two ways to receive notification:
when the app is closed/inactive and you get the payload in didFinishLaunchingWithOptions:
when the application is open/active and currently visible you get payload in didReceiveRemoteNotification:
When your application is closed, iOS take care of displaying a notification (if notification extension not developed) and launch your app when you tap on it.
If your application is open you have to take care of displaying this notification or ignore it.
Here what official documentation says:
When local and remote notifications are not handled directly by your
app or the user, they are displayed in Notification Center so that
they can be viewed later. Use the
getDeliveredNotificationsWithCompletionHandler: method of the shared UNUserNotificationCenter object to get a list of
notifications still being displayed in Notification Center. If you
find any notifications that are now outdated and should not be
displayed to the user, you can remove them using the
removeDeliveredNotificationsWithIdentifiers: method.
official documentation
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 :)
In Info.plist, I have added FirebaseAppDelegateProxyEnabled to NO (boolean), and capabilities for Background Modes --> Remote Notification is ON and Push Notification is ON too.
And on the iPhone device, has running with dialog to "allow notification"..
I have some codes like:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let gcmBroadcast = "gcm.broadcast"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Messaging.messaging().delegate = self
// Use Firebase library to configure APIs
let firebasePListFileName = ControllerEnvironment.getGoogleService()
let firebaseOptions = FirebaseOptions(contentsOfFile: Bundle.main.path(forResource: firebasePListFileName, ofType: "plist")!)
FirebaseApp.configure(options: firebaseOptions!)
// firebase cloud messaging
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()
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[gcmBroadcast] {
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[gcmBroadcast] {
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[gcmBroadcast] {
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[gcmBroadcast] {
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]
}
and logs:
Firebase registration token: f6MUKT270iw:APA9.......Eg
APNs token retrieved: 32 bytes
I tried sending Push using firebase cloud messaging console as: User segment -> app and got status completed in firebase list of messages.
But, actually my iPhone did not receive the notification.
How can I solve the issue?
On iOS the app needs to be not in foreground, as otherwise the developer is informed of the push notification and can manually display it / handle it otherwise -> make sure that the app is not in the foreground
You also need to add an APNs key or APNs certificates to Firebase.