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:
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 inWindows > Devices and Simulator
for my iPhone instead of the device token I get back in the console from this method: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.