0

I implemented the procedures in order to use Firebase Cloud Messaging service. I set

FirebaseAppDelegateProxyEnabled to NO (swizzling disabled)

because I want to customize the message to send to the device by using the structure "data" instead of "notification" structure. I can see from the debug output that a message is received in this callback:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

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


    // I want to play a sound
    // create a sound ID, in this case its the SMSReceived sound.
    let systemSoundID: SystemSoundID = 1007 // file:// /System/Library/Audio/UISounds/sms-received1.caf
    // to play sound
    AudioServicesPlaySystemSound(systemSoundID)
    if let title = userInfo["title"] as? NSString {

          let myalert = MyAlert(title: "New message received!", message: title as String, preferredStyle: UIAlertControllerStyle.alert)

          myalert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
          myalert.addAction(UIAlertAction(title: "Open app", style: .default, handler: { _ in
          print("The \"OK\" alert occured.")
          }))
          self.window?.rootViewController?.present(myalert, animated: true, completion: nil)

    }// END if let title = alert["title"]

    completionHandler(UIBackgroundFetchResult.newData)
}

It raises in both cases (fore and back). Everything goes perfectly in foreground but no sound and no alert in background. I am pretty sure I missed something but I cannot understand what... Maybe I need some "authorization" from the operating system to display and alert and play sound?


EDIT 1: I implemented the UNUserNotificationCenterDelegate to requestAuthorization:

func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: 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]

        if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
            // show alert
            let myalert = MyAlert(title: "New message!", message: "MyApp" as String, preferredStyle: UIAlertControllerStyle.alert)

            myalert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
            myalert.addAction(UIAlertAction(title: "Open MyApp", style: .default, handler: { _ in
                print("The \"OK\" alert occured.")
            }))
            self.window?.rootViewController?.present(myalert, animated: true, completion: nil)
        }


        return true
    }
cicaletto79
  • 179
  • 1
  • 12
  • 1. you don't need such comments. It's quite obvious. 2. You basically need to request permission. See [here](https://stackoverflow.com/a/39383027/5175709) and then decide what to show when the notification arrives. See [here](https://stackoverflow.com/a/39642069/5175709). I'm not sure why you're not using the built-in system for showing the alert and sound. Why are you creating your own alert?! – mfaani Feb 21 '18 at 17:38
  • Thank you @Honey for your answer, I already do that like this as you can read in EDIT 1.... – cicaletto79 Feb 22 '18 at 09:00
  • @Honey I didn't answer your question about why I'm not creating a built-in system (notification structure).. just because I cannot play sound when app is in background! – cicaletto79 Feb 22 '18 at 13:30
  • "I cannot play sound when app is in background!" Who said that. If I remember correctly you can create a push notification and include the sound to its payload + add the sound file to the app bundle. See [here](https://stackoverflow.com/questions/37536591/change-push-notification-sound).(I removed my previous comment.It was incorrect). If you need to have the sound in foreground to then you must have `completionHandler([.alert, .sound])` as well in your wilPresentNotification. See [this tutorial](https://code.tutsplus.com/tutorials/an-introduction-to-the-usernotifications-framework--cms-27250) – mfaani Feb 22 '18 at 21:35

1 Answers1

4

SOLVED! I used the built-in system with a "notification" structure with swizzling enabled (FirebaseAppDelegateProxyEnabled = YES in info.plist) and here is the json message I created:

{
"message":
     {
          "topic":"topic",
          "notification":
               {
                    "body":"body",
                    "title":"title"
               },
          "apns":
               {
                    "payload":
                         {
                              "aps":
                                   {
                                        "sound":"default"
                                   }
                         }
               }
     }
}

This is the AppDelegate.swift class I used...

import UIKit
import StoreKit
import UserNotifications
import Firebase
import AVFoundation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate,MessagingDelegate {



var window: UIWindow?

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

    self.registerForRemoteNotification()

    //Firebase initialization
    FirebaseApp.configure()
    // To receive registration tokens on app start we need to implement the messaging delegate protocol in
    // a class and provide it to the delegate property after calling FirebaseApp.Configure
    Messaging.messaging().delegate = self

    let token = Messaging.messaging().fcmToken
    print("FCM TOKEN = \(String(describing: token))")

    if let remote = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification]  as? [AnyHashable : Any] {
        self.sendToNewsPage()
    }

    return true
}

func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
    //
    print("Application didRegister notificationSettings")
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    // Disable swizzling, explicitly map the APNs token to the FCM registration token.
    //Messaging.messaging().apnsToken = deviceToken
    Messaging.messaging().subscribe(toTopic: "topic")
    print("Subcribed to topic")
}

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

// MARK: Remote Notifications
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
    #if DEBUG
        print(userInfo)
    #endif

    let state: UIApplicationState = UIApplication.shared.applicationState
      if state == .active {
        // create a sound ID, in this case its the SMSReceived sound.
        let systemSoundID: SystemSoundID = 1007
        // to play sound
        AudioServicesPlaySystemSound(systemSoundID)

        if let aps = userInfo["aps"] as? NSDictionary {
            if let alert = aps["alert"] as? NSDictionary {
                if let title = alert["title"] as? NSString {

                    let myalert = MyAlert(title: "New message received!", message: title as String, preferredStyle: UIAlertControllerStyle.alert)

                    myalert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
                    myalert.addAction(UIAlertAction(title: "Open ViewController", style: .default, handler: { _ in
                        print("The \"OK\" alert occured.")
                        self.sendToNewsPage()
                    }))
                    self.window?.rootViewController?.present(myalert, animated: true, completion: nil)

                }// END if let title = alert["title"]
            }// END if let alert = aps["alert"]
        }// END if let aps = userInfo["aps"]
    }// END else if state == .active
      else {
            self.sendToNewsPage()
    }
}

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {

    print("Firebase NEW registration token: \(fcmToken)")
    // Note: This callback is fired at each app startup and whenever a new token is generated.

}

func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
    print("Firebase registration token REFRESH: \(fcmToken)")

}

func registerForRemoteNotification()  {
    let application:UIApplication = UIApplication.shared

    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()

}

    // We need to implement this code only if swizzling id disabled - (FirebaseAppDelegateProxyEnabled = NO)
//    @available(iOS 10.0, *)
//    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//
//        completionHandler([.alert, .badge, .sound])
//    }
//
//    @available(iOS 10.0, *)
//    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
//
//        completionHandler()
//    }



//MARK : Notification

func sendToNewsPage() {
    let newsMessage = UIStoryboard(name: "MenuInfo", bundle: nil).instantiateViewController(withIdentifier: "SWRevealViewController")
    self.window?.rootViewController  = newsMessage
}

}

This json let the notification play a sound in background!

cicaletto79
  • 179
  • 1
  • 12