2

After reading a lot of answers I'm posting this here because my issue is still unknown to me. If I send notification from firebase console I get the notification but if I go to my app backend and perform a function which generates notification according to the backend guy I am not getting notifications. The android developer is getting the notification. So as I've no knowledge which information might be of use I'm pasting my AppDelegate.swift class here:

import UIKit
import UserNotifications
import GoogleMaps
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let gcmMessageIDKey = "gcm.message_id"

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        UITabBar.appearance().tintColor = UIColor.white

        UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.lightGray], for:.normal)
        UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.black], for:.selected)

        GMSServices.provideAPIKey("My_GOOGLE_MAP_ID_HERE")

        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
        ///when on foreground this function runs
        // 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([.badge, .sound, .alert])
    }

    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) {
        print("Firebase registration token: \(fcmToken)")
        let defaults = UserDefaults.standard
        defaults.set(Messaging.messaging().fcmToken, forKey: "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)")

        let d = remoteMessage.appData["data"] as! String
        print(d)
    }
    // [END ios_10_data_message]
}

If I send them from firebase console I get them. But if the backend of the app generates a notification I'm not getting it. As per Firebase documentation if the backend guy has the FCM he's good to go to send the notification and he has the correct FCM I've checked that too.

Upon explaining this scenerio he told me that the same was happening for the android guy before (firebase notification is received but notifications from server are not) and then it turns out the issue was at android guy end. He was not presenting notifications when he was receiving one, so as per his words the issue is at my end that I'm not presenting the notification when I'm getting it (which I'm not receiving from server because when I get it from firebase it displays on lock screen). Also is there any certificate that he need to implement in order for that? Because I've a certificate which I've uploaded on firebase.

This is the payload:

Array
(
    [data] => Array
        (
            [title] => Approved
            [doctor_name] => Dr. Smith
            [time] => 07:00 PM
            [date] => 2017-10-19
            [status] => Approved
            [is_background] => 1
            [message] => Dear Chaudhry Talha, You appointment has been Approved By Dr. Smith on 2017-10-19 at 07:00 PM
            [payload] => Array
                (
                    [title] => Approved
                    [description] => Dear Chaudhry Talha, You appointment has been Approved By Dr. Smith on 2017-10-19 at 07:00 PM
                )

            [timestamp] => 2017-10-20 12:27:45
        )

    [type] => 2
)
Chaudhry Talha
  • 7,231
  • 11
  • 67
  • 116
  • You need to create local notifications here – Muhammad Shahzad Oct 18 '17 at 08:13
  • @SidShehxad how to do that can you please tell me that I'm new with firebase? – Chaudhry Talha Oct 18 '17 at 08:14
  • Are you sure your application is not running or in the background when the data print? – Salman Ghumsani Oct 18 '17 at 08:16
  • @SalmanGhumsani no the app is running in the foreground when this print is triggered. I also want to know how to trigger this when the app is running in background or not running. – Chaudhry Talha Oct 18 '17 at 08:17
  • https://www.cocoacontrols.com/controls/swiftynotifications this will help you to create local notifications – Muhammad Shahzad Oct 18 '17 at 08:17
  • @SidShehxad will this work when the app is not running or in background? If no then how can I counter that? – Chaudhry Talha Oct 18 '17 at 08:18
  • On the foreground, the notification will not show you have to make the custom view to display the notification when your app is foreground. By default, it will display when your app is not running or in background. – Salman Ghumsani Oct 18 '17 at 08:19
  • if your app is in background or not running than you will receive a remote notification but when you are in the app you need to create local notifications. – Muhammad Shahzad Oct 18 '17 at 08:20
  • @SalmanGhumsani this is the problem by default it is not displaying notification when the app is not running or in background. As soon as I open the app it instantly open goes in this function. – Chaudhry Talha Oct 18 '17 at 08:21
  • than you must be missing something in your app-delegate where you need to handle background notifications. – Muhammad Shahzad Oct 18 '17 at 08:23
  • https://github.com/firebase/quickstart-ios/blob/master/messaging/MessagingExampleSwift/AppDelegate.swift#L160-L162 check this firebase example there must be something you are missing in your code. – Muhammad Shahzad Oct 18 '17 at 08:24
  • @SidShehxad let me know if there is any more code you want to see I'll update it in my question. Do you mean `didReceiveRemoteNotification` method? – Chaudhry Talha Oct 18 '17 at 08:24
  • In capabilities turn on push notification and background modes (Remote notifications). Are they already on ? – Sharad Chauhan Oct 18 '17 at 08:32
  • @SidShehxad as I implemented the whole code as per the link you provided, in extensions and all the other code it is not triggering `didRecieve` method in that case. – Chaudhry Talha Oct 18 '17 at 08:54
  • @sharadchauhan yes both of them are on. – Chaudhry Talha Oct 18 '17 at 08:54
  • https://github.com/firebase/quickstart-ios/issues/286 can try this – naga Oct 18 '17 at 11:05
  • @naga I tried this but still didn't work. Please see my updated question – Chaudhry Talha Oct 18 '17 at 11:41
  • @SalmanGhumsani please see the updated version of my question. – Chaudhry Talha Oct 18 '17 at 11:41
  • how does your payload look like? Do you have `content-available` set to `1`? (I'm not sure how firebase handles that, it might have an _alias_ key. For more see 'first two' answers from [**here**](https://stackoverflow.com/questions/37570200/firebase-silent-apns-notification)). For more on content-available see my answer [here](https://stackoverflow.com/questions/42275060/what-is-difference-between-remote-notification-and-silent-notification-in-ios/42302369#42302369) and search for content-available... – mfaani Oct 18 '17 at 13:24

6 Answers6

1

can u try this most it's help full // Receive displayed notifications for iOS 10 devices.

func userNotificationCenter(_ center: UNUserNotificationCenter,
                        willPresent notification: UNNotification,
                        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // Change this to your preferred presentation option
    completionHandler([.alert, .badge, .sound]) // <-----
}
naga
  • 397
  • 2
  • 12
  • 26
  • I replaced my `willPresent` method to this but still as I open my app it instantly show me the notification. This code of mine is only working for foreground. :/ – Chaudhry Talha Oct 18 '17 at 12:06
1

I think it is due to the structure of the notification, at least if I understand the question. I had the same problem once, the back-end guys were arguing that it is working on Android. But it is covered in the documentation.

Structure should look like described here

"notification" : {
  "body" : "great match!",
  "title" : "Portugal vs. Denmark"
}

Whatever they put in data will still be received, but will not be used to present notification(on Android it will present). There is no need for extracting anything just tell the backend guys to add this for you.

Luzo
  • 1,336
  • 10
  • 15
  • okay great let me send this to the backend guy and see if this works. – Chaudhry Talha Oct 23 '17 at 08:14
  • Just don't forget to tell that it can't be wrapped in data it has to be on the same level as data. Plus you can try it yourself first with Postman or any other tool. – Luzo Oct 23 '17 at 08:31
1

Before you send notification, please make sure that you have added "content_available" : true in the JSON payload. Otherwise you won't get push notification in the background mode.

"notification" : {
            "content_available" : true,
            "body" : "this is body",
            "title" : "this is title"
}
Rupom
  • 429
  • 5
  • 10
  • `content_available` is for "silent push", not push notifications while the app is in the background. Silent push is used to trigger content fetches without presenting a user interface such as an alert. – quellish Oct 24 '17 at 04:56
0
    import UIKit
    import UserNotifications
    import FirebaseMessaging
    import Firebase

    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    let gcmMessageIDKey = "gcm.message_id"

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        // 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 })

            // For iOS 10 data message (sent via FCM)
            FIRMessaging.messaging().remoteMessageDelegate = self

        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }

        application.registerForRemoteNotifications()

        // [END register_for_notifications]


        FIRApp.configure()

        // [START add_token_refresh_observer]
        // Add observer for InstanceID token refresh callback.
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.tokenRefreshNotification),
                                               name: .firInstanceIDTokenRefresh,
                                               object: nil)
        // [END add_token_refresh_observer]

        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
        // 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
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        // Print full message.
        print(userInfo)

        completionHandler(UIBackgroundFetchResult.newData)
    }

    // [START refresh_token]
    func tokenRefreshNotification(_ notification: Notification) {
        if let refreshedToken = FIRInstanceID.instanceID().token() {
            print("InstanceID token: \(refreshedToken)")
        }

        // Connect to FCM since connection may have failed when attempted before having a token.
        connectToFcm()
    }
    // [END refresh_token]

    // [START connect_to_fcm]
    func connectToFcm() {
        // Won't connect since there is no token
        guard FIRInstanceID.instanceID().token() != nil else {
            return
        }

        // Disconnect previous FCM connection if it exists.
        FIRMessaging.messaging().disconnect()

        FIRMessaging.messaging().connect { (error) in
            if error != nil {
                print("Unable to connect with FCM. \(error?.localizedDescription ?? "")")
            } else {
                print("Connected to FCM.")
            }
        }
    }
    // [END connect_to_fcm]

    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 InstanceID token.
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("APNs token retrieved: \(deviceToken)")

        // With swizzling disabled you must set the APNs token here.
        // FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.sandbox)
    }

    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.
        connectToFcm()
    }

    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.
        FIRMessaging.messaging().disconnect()
        print("Disconnected from FCM.")
    }

    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:.
    }
}

// [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.
        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]
    // [START ios_10_data_message_handling]
    extension AppDelegate : FIRMessagingDelegate {
    // Receive data message on iOS 10 devices while app is in the 
     foreground.
    func applicationReceivedRemoteMessage(_ remoteMessage: 
    FIRMessagingRemoteMessage) {
        print(remoteMessage.appData)
    }
   }
    // [END ios_10_data_message_handling]
0

Have you tried adding this to the didFinishLaunchingWithOptions:

// Check if launched from notification
        // 1
        if let notification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? [String: AnyObject] {
            // 2
            let aps = notification["aps"] as! [String: AnyObject]
            // 3
            delay(1.0, closure: {
                NSNotificationCenter.defaultCenter().postNotificationName("openedFromPush", object: nil, userInfo: ["info":notification])
            })

        }
danialzahid94
  • 4,103
  • 2
  • 19
  • 31
0

upload your certificate or p8 file again on firebase console.