Why does push notification ios work after rebooting the device? - ios

I am trying to implement push notification via FCM (Firebase Cloud Messaging). Right now, I'm using Messaging.messaging().subscribe for background push notification.
But somehow one minute the push notification works and after a while it doesn't work completely. Then, when I reboot the device, push notification starts working again. What would be the problem??
This is the basic setup in AppDelegate
import UIKit
import UserNotifications
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let gcmMessageIDKey = "gcm.message_id"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
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)
}
Messaging.messaging().delegate = self
// Messaging.messaging().unsubscribe(fromTopic: "dev_store_abcde-acbcd-adbcd-bcddd") { error in
// print("unsub")
// }
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.hexString
print(deviceTokenString)
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)")
}
}
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
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: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
Messaging.messaging().subscribe(toTopic: "dev_store_id") { error in
print("Subscribe to dev_store_id")
}
// 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 messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Message Data", remoteMessage.appData)
}
}
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print(userInfo["value"])
UserDefaults.standard.set(true, forKey: "isNotified")
UserDefaults.standard.synchronize()
completionHandler(.newData)
}
}
extension Data {
var hexString: String {
let hexString = map { String(format: "%02.2hhx", $0) }.joined()
return hexString
}
}

Turns out after turning on/off wifi seems to work for the development build. If notification is deployed to the distribution build, silent notification is working considerably well.

Related

APNS device token not set before retrieving FCM Token for Sender ID ''. Notifications to this FCM Token will not be delievered over APNS. - IOS

I am trying to enable push notifications using firebase and this is the document I referred. I followed everything till the end of step 4.
This is my PushNotificationManager.swift file
import Foundation
import FirebaseFirestore
import FirebaseMessaging
import UIKit
import UserNotifications
import FirebaseCore
class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate {
let userID: String
init(userID: String) {
self.userID = userID
super.init()
}
func registerForPushNotifications() {
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 })
// For iOS 10 data message (sent via FCM)
Messaging.messaging().delegate = self
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
UIApplication.shared.registerForRemoteNotifications()
updateFirestorePushTokenIfNeeded()
}
func updateFirestorePushTokenIfNeeded() {
if let token = Messaging.messaging().fcmToken {
let usersRef = Firestore.firestore().collection("users_table").document(userID)
usersRef.setData(["fcmToken": token], merge: true)
print(token)
}
}
// func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
// print(remoteMessage.appData)
// }
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
updateFirestorePushTokenIfNeeded()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print(response)
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Registered for Apple Remote Notifications")
Messaging.messaging().setAPNSToken(deviceToken, type: .unknown)
}
}
This is the AppDelegate.m
mport UIKit
import FirebaseCore
import FirebaseAuth
import FirebaseFirestore
import FirebaseMessaging
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
let db = Firestore.firestore()
let pushManager = PushNotificationManager(userID: "currently_logged_in_user_id")
pushManager.registerForPushNotifications()
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
application.registerForRemoteNotifications()
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
Even though I am getting the FCM token printed as per the print statement in PushNotificationManager.swift. However I am getting the following in the console
APNS device token not set before retrieving FCM Token for Sender ID 'XXXXXXXXX'. Notifications to this FCM Token will not be delivered over APNS.Be sure to re-retrieve the FCM token once the APNS device token is set.
I have tried all the solutions mentioned on this post however I am not able to get past the error. Can someone tell me what I could be missing.

How to get device token using firebase?

In old projects I have receiving the device token when app install first time or refresh the token. But now I create a new project and write code for did register device token delegate and ask permission but now did register device token is not called in swift version 4.2 . Has anyone faced this issue? If yes, what is solution?
import UIKit
import FirebaseInstanceID
import GoogleMaps
import GooglePlaces
import UserNotifications
import FirebaseCore
import FirebaseMessaging
import Alamofire
import Fabric
import Crashlytics
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate,MessagingDelegate,CLLocationManagerDelegate {
var window: UIWindow?
let locationManager = CLLocationManager()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let statusbar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
statusbar.backgroundColor = UIColor.fromHexaString(hex: "feac1c")
}
UIApplication.shared.statusBarStyle = .lightContent
Fabric.with([Crashlytics.self])
let remoteNotif = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? NSDictionary
if remoteNotif != nil
{
}
else
{
print("Not remote")
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
GMSServices.provideAPIKey(google_url_links().google_mapKey)
GMSPlacesClient.provideAPIKey(google_url_links().google_mapKey)
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
Messaging.messaging().delegate = self
})
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
FirebaseApp.configure()
NotificationCenter.default.addObserver(self, selector: #selector(appDelegate.tokenRefereshNotification), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
// Override point for customization after application launch.
return true
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print(remoteMessage.appData)
}
// func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
//
// print("Firebase registration token: \(fcmToken)")
// UserDefaults.standard.set(fcmToken, forKey: "DeviceToken")
//
//
// }
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
Messaging.messaging().apnsToken = deviceToken
//FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .none)
// Messaging.messaging().setAPNSToken(deviceToken, type: .sandbox)
}
#objc func tokenRefereshNotification()
{
let refereshtoken = InstanceID.instanceID().token() ?? ""
print("token23123::::\(refereshtoken)")
UserDefaults.standard.set(refereshtoken, forKey: "deviceToken")
connectToFCM()
}
func connectToFCM()
{
guard InstanceID.instanceID().token() != nil else
{
return
}
Messaging.messaging().disconnect()
Messaging.messaging().connect { (error) in
if (error != nil)
{
print("error unable to connect\(String(describing: error))")
}
else
{
print("connect to fcm")
}
}
}
#objc func CheckInterntConnection()
{
let alert = UIAlertController(title: "", message: custom_message().error_internet, preferredStyle: UIAlertController.Style.actionSheet)
alert.addAction(UIAlertAction(title: custom_message().OK, style: UIAlertAction.Style.default, handler: nil))
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
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) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
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.
}
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) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
You need to do this too get firebase Token
Declare a variable in your AppDelegate class.
var firebaseToken: String = ""
Call these methods in your didFinishLaunchingWithOptions function
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
self.registerForFirebaseNotification(application: application)
Messaging.messaging().delegate = self
return true
}
Add this function in your AppDelegate class.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
func registerForFirebaseNotification(application: UIApplication) {
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()
}
}
And Last create an extension of AppDelegate and add these functions
extension AppDelegate: MessagingDelegate, UNUserNotificationCenterDelegate {
//MessagingDelegate
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
self.firebaseToken = fcmToken
print("Firebase token: \(fcmToken)")
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("didReceive remoteMessage: \(remoteMessage)")
}
//UNUserNotificationCenterDelegate
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("APNs received with: \(userInfo)")
}
}
The same methods in swift will help you to trace token delegate data
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(#"Unable to register for remote notifications: %#", error);
}
// 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 device token can be paired to
// the FCM registration token.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"APNs device token retrieved: %#", deviceToken);
// With swizzling disabled you must set the APNs device token here.
[FIRMessaging messaging].APNSToken = deviceToken;
}
You have to call the following function to get fcm token,
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
UserDefaults.standard.set(fcmToken, forKey: "DeviceToken")
}
and edit your didFinishLaunchingWithOptions() of appDelegate as,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
if(launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] != nil){
}
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)")
}
}
Messaging.messaging().isAutoInitEnabled = true
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert,.sound] // .badge,
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert,.sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
Messaging.messaging().delegate = self
return true
}
Messaging.messaging().token { token, tokenGenerationError in
if let token = token{
print("the token is \(token)")
}
if let tokenError = tokenGenerationError{
print("the tokenError is \(tokenError)")
}
}
You just code these lines in didFinishLaunchingWithOptions and then run then you can see the Token in print console and use that token in Firebase.

Firebase messaging doesn't work after app reinstalls

I am running into a very weird problem. After uninstalling and reinstalling my iOS 11 app (coded in swift), I have to launch the app multiple times (10 to 20) before my firebase messaging works again. I change nothing in the code (sometimes I just wait and hour or so) and for some reason it seems that Firebase Messaging Notifications only work after relaunching my app multiple times. It's very important for my app to be able to receive notifications right when it's first opened as my app is basically reliant on them. I just need to find a way to get notifications to work when I first launch the app after I install it. I've attached images of my code. (I have method_swizzling enabled)
If someone could please help me I would really appreciate it. I've seen youtube videos doing the same things I'm doing and they're able to get messages to work right away. I've tried a lot of what I've seen online and other questions that seem to be like this but none of their methods seem to work. Thank you so much to anyone who tries to help!
[edit]
This is the code for the application launch function:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
application.statusBarStyle = ColorScheme.isDark ? .lightContent : .default
FirebaseApp.configure()
// Messaging.messaging().delegate = self
// Messaging.messaging().shouldEstablishDirectChannel = true
// UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in }
// application.registerForRemoteNotifications()
setupMessaging(application: application)
}
This is the code for actually connecting and setting up my Firebase Messaging:
extension AppDelegate {
func setupMessaging(application: UIApplication) {
Messaging.messaging().delegate = self
Messaging.messaging().shouldEstablishDirectChannel = true
print("channel established", Messaging.messaging().isDirectChannelEstablished)
if #available(iOS 10.0, *) {
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()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().setAPNSToken(deviceToken, type: .sandbox)
//TODO: Change for release
}
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
}
}
And this is the debug functions that are called when I receive the notification:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("I got a notification")
print(userInfo)
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("remote message received")
}
P.S. my AppDelegate extends MessagingDelegate and UNUserNotificationCenterDelegate
Please Check the below code
import Firebase
import FirebaseMessaging
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
Messaging.messaging().delegate = self
//REMOTE NOTIFICATION
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
print("FCM token: \(token ?? "")")
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
} else {
// Fallback on earlier versions
}
return true
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken as Data
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
// Print full message.
print(userInfo)
}
// This method will be called when app received push notifications in foreground
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler([UNNotificationPresentationOptions.alert,UNNotificationPresentationOptions.sound,UNNotificationPresentationOptions.badge])
}
// MARK:- Messaging Delegates
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
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)")
}
}
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("received remote notification")
}
}
I found the answer! The problem was that I was using the online console firebase provides to send my test messages. I guess it time to register devices. I switched over to firebase functions to send my notification and then subscribed to a test topic with my phone and that fixed everything. I get notifications on first app launch now and I'm happy

Swift 4, Firebase 5.8.0 FCM token nil

I am setting up push notifications, and everything is going well until I try to get the FCM token so I can send a test message to an actual device. Using the pods Firebase 5.8.0, FirebaseCore (5.1.3), FirebaseInstanceID (3.2.1), and FirebaseMessaging (3.1.2), I can get the APNS token fine but every time I try to get the FCM token, it comes out as nil or when I use InstanceID.instanceID().instanceID(handler:) it results in some timeout error code 1003 and a result of nil. didReceiveRegistrationToken does not get called either. I've tried a many 2017 solutions but they're either deprecated or don't work. MessageDelegate and UNUserNotificationCenterDelegate are set, push notifications, and keychain sharing are enabled in capabilities, method swizzling is off via p-list and Here is my didRegisterForRemoteNotificationsWithDeviceToken function where I correctly get the apns token. Any ideas? Thanks.
`public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Registration Successful: Device token \(deviceToken)")
Messaging.messaging().apnsToken = deviceToken
print("Messaging APNS Token:\(Messaging.messaging().apnsToken)")
print("Instance Id: \(InstanceID.instanceID().token())") //deprecated & returns nil
print("Messaging APNS Token:\(Messaging.messaging().apnsToken)")
print("Token:\(Messaging.messaging().fcmToken)") // return nil
InstanceID.instanceID().instanceID { (result, error) in
if let result = result {
print("Result:\(String(describing: result?.token))")
} else {
print("Error:\(error))")
} //returns after about 2 mins with an firebase iid error of 1003
}
Please check the below app delegate code.
import UIKit
import Firebase
import FirebaseMessaging
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
//REMOTE NOTIFICATION
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
print("FCM token: \(token ?? "")")
//Added Code to display notification when app is in Foreground
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
} else {
// Fallback on earlier versions
}
return true
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken as Data
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
// Print full message.
print(userInfo)
}
// This method will be called when app received push notifications in foreground
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler([UNNotificationPresentationOptions.alert,UNNotificationPresentationOptions.sound,UNNotificationPresentationOptions.badge])
}
// MARK:- Messaging Delegates
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
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)")
}
}
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("received remote notification")
}
}

Firebase FCM not working initially but works after 3-4 times if the app is opened

I am new to Firebase FCM Integration and i am facing some issues. When the app is installed for first time i don't receive Push Notification but if i open and close app for few times and then send push notification from Firebase i am receiving notification without any change in client or server code. Can anyone help me out on this?
I have attached my appdelegate code below
import UIKit
import Firebase
import UserNotifications
import FirebaseMessaging
import FirebaseInstanceID
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate,MessagingDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: 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: { (granted, error) in
if error == nil{
UIApplication.shared.registerForRemoteNotifications()
}
})
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
}
application.registerForRemoteNotifications()
return true
}
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
}
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
UIApplication.shared.registerForRemoteNotifications()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// I have tried both method but none worked and i also tried MessagingAPNSTokenType.sandbox and prod
// Method 1: Messaging.messaging().apnsToken = deviceToken
// Method 2:
Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.unknown)
}
}
Thanks in advance
I have the same issue as you have and there is a known issue where the FCM token is not always being associated with the APNs device token if UIApplication.shared.registerForRemoteNotifications() is not called early enough.
According to this GitHub thread this issue has already been fixed in the FirebaseInstanceID SDK, and should be coming out soon.
In the meantime, you can:
Lock your FirebaseInstanceID pod to 2.0.0 in your Podfile, or
Ensure you're calling UIApplication.shared.registerForRemoteNotifications() on app start, ideally before FirebaseApp.configure().
Update:
I just made some additional tests and I noticed that this is a bit random of when this will work. For some devices it worked immediately for some not. For some devices Single Token push worked and not User Segment. I managed to make it work so that you at least either get Single Token push or User Segment by adding the following:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in
})
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
// For iOS 10 data message (sent via FCM)
Messaging.messaging().delegate = self
application.registerForRemoteNotifications()
print("::: registerForRemoteNotifications iOS 10")
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
print("::: registerUserNotificationSettings iOS 9")
}
FirebaseApp.configure()
Messaging.messaging().delegate = self
Messaging.messaging().shouldEstablishDirectChannel = true
if let refreshedToken = InstanceID.instanceID().token() {
print("::: InstanceID token: \(refreshedToken)")
}
NotificationCenter.default.addObserver(self, selector: #selector(tokenRefreshNotification), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().setAPNSToken(deviceToken, type: .prod)
if let refreshedToken = InstanceID.instanceID().token() {
print("InstanceID token: \(refreshedToken)")
}
}
The thing is that it won´t work if you don´t access the didRegisterForRemoteNotificationsWithDeviceToken function.
Firebase FCM, it takes sometime to generate its first token. After getting this token you will start receiving push notification. Check these out. It will be clear for sure:
Check this answer
AppDelegate implementation with FCM
Hope this helps.
Update your appdelegate by this
import FirebaseCore
import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.fcmInitialSetup(application)
return true
}
func fcmInitialSetup(_ application: UIApplication){
// [START register_for_notifications]
if #available(iOS 10.0, *) {
let uns: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(uns)
application.registerForRemoteNotifications()
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
// [END register_for_notifications]
FIRApp.configure()
// Add observer for InstanceID token refresh callback.
NotificationCenter.default.addObserver(self, selector: #selector(self.tokenRefreshNotification), name: NSNotification.Name.firInstanceIDTokenRefresh, object: nil)
if let token = FIRInstanceID.instanceID().token() {
sendTokenToServer(token)
}
}
func sendTokenToServer(_ currentToken: String) {
print("sendTokenToServer() Token: \(currentToken)")
// Send token to server ONLY IF NECESSARY
print("InstanceID token: \(currentToken)")
self.token = currentToken
UserDefaults.standard.set(self.token, forKey: "token")
UserDefaults.standard.synchronize()
if self.token != nil{
let userInfo = ["token": self.token]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
}
}
// NOTE: Need to use this when swizzling is disabled
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenChars = (deviceToken as NSData).bytes.bindMemory(to: CChar.self, capacity: deviceToken.count)
var tokenString = ""
for i in 0..<deviceToken.count {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.unknown)
print("Device Token:", tokenString)
print("FIRInstanceID.instanceID().token() Token:", FIRInstanceID.instanceID().token())
if let tokenData = FIRInstanceID.instanceID().token(){
UserDefaults.standard.set(tokenData, forKey: "token")
UserDefaults.standard.synchronize()
let userInfo = ["token": tokenData]
}
}
func tokenRefreshNotification(_ notification: Notification) {
// NOTE: It can be nil here
// print("Token:\(FIRInstanceID.instanceID().token()!)")
if let refreshedToken = FIRInstanceID.instanceID().token() {
print("InstanceID token: \(refreshedToken)")
UserDefaults.standard.set(refreshedToken, forKey: "token")
UserDefaults.standard.synchronize()
print("update now \(self.token)")
if self.token != nil{
let userInfo = ["token": self.token]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
}
}
// Connect to FCM since connection may have failed when attempted before having a token.
connectToFcm()
}
// [END refresh_token]
func connectToFcm() {
FIRMessaging.messaging().connect { (error) in
if (error != nil) {
print("Unable to connect with FCM. \(error)")
} else {
print("Connected to FCM.")
}
}
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
print(userInfo)
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
print("Within open URL")
return true
}
// [START receive_apns_token_error]
func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError
error: Error ) {
print("Registration for remote notification failed with error: \(error.localizedDescription)")
// [END receive_apns_token_error]
let userInfo = ["error": error.localizedDescription]
NotificationCenter.default.post(
name: Notification.Name(rawValue: rkey), object: nil, userInfo: userInfo)
}
func registrationHandler(_ token: String!, error: NSError!) {
if (token != nil) {
self.token = token!
print("Registration Token: \(self.token)")
UserDefaults.standard.set(self.token, forKey: "token")
UserDefaults.standard.synchronize()
let userInfo = ["token": self.token]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
} else {
print("Registration to GCM failed with error: \(error.localizedDescription)")
let userInfo = ["error": error.localizedDescription]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
}
}
func registerForPushNotifications(_ application: UIApplication) {
let notificationSettings = UIUserNotificationSettings(
types: [.badge, .sound, .alert], categories: nil)
application.registerUserNotificationSettings(notificationSettings)
}
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
if notificationSettings.types != UIUserNotificationType() {
application.registerForRemoteNotifications()
}
}
// [START receive_message]
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
// Print message ID. add Toast
print(userInfo);
print(application.keyWindow?.visibleViewController() ?? "")
print("Message ID: \(userInfo["gcm.message_id"]!)")
// Print full message.
print("%#", userInfo)
}
// [END receive_message]
func applicationDidBecomeActive(_ application: UIApplication) {
connectToFcm()
}
// [START disconnect_from_fcm]
func applicationDidEnterBackground(_ application: UIApplication) {
// FIRMessaging.messaging().disconnect()
// print("Disconnected from FCM.")
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
}
// [END disconnect_from_fcm]
// [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
// Print message ID.
print("Message ID: \(userInfo["gcm.message_id"]!)")
// Print message ID. add Toast
// Print full message.
print("%#", userInfo)
// Print full message.
print("%#", userInfo)
}
}
extension AppDelegate : FIRMessagingDelegate {
// Receive data message on iOS 10 devices.
func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) {
print("%#", remoteMessage.appData)
}
}
NotificationCenter.default.addObserver(self selector:#selector(self.getFcmToken), name: .firInstanceIDTokenRefresh,
object: nil)
put this in application did finsih launching method
and make a function
func getFcmToken(notification: NSNotification) {
guard let contents = FIRInstanceID.instanceID().token()
else {
return
}
print("InstanceID token: \(contents)")
if let token = FIRInstanceID.instanceID().token(){
print(token)
}

Resources