首页 > 解决方案 > 使用 sendToDevice 从 Firebase 功能发送到 iOS 设备时,如何使推送通知正常工作?

问题描述

我正在尝试使推送通知在 iOS 上工作。使用“send”时它们可以通过,但使用“sendToDevice”时不会通过。这真的很奇怪。我已遵循此处的所有说明(https://firebase.google.com/docs/cloud-messaging/ios/client),但它仍然无法正常工作。我已经在我的开发者帐户和 Firebase 控制台中设置了 APNs 文件,我可以直接从 Firebase 仪表板发送推送。我附上了我的 firebase 函数代码以及我的 AppDelegate。我究竟做错了什么?谢谢您的帮助。


import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    let notificationCenter = UNUserNotificationCenter.current()
    let gcmMessageIDKey = "gcm.message_id"

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        FirebaseApp.configure()
        
        Messaging.messaging().delegate = self

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

    // 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: \(String(describing: fcmToken))")

        //todo see if i need this code here
      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.
        UserDefaults.standard.setValue(fcmToken, forKey: "LATEST_FCM_TOKEN")

        if(Auth.auth().currentUser != nil){
            self.updateUsersFcmToken(token: fcmToken!)
        }else{
            
        }
    }
    
        private func updateUsersFcmToken(token: String) {
                        
            Firestore.firestore().collection("Users").document(Auth.auth().currentUser!.uid).collection("fcmTokens").document(token).setData([
                "token": token, "os": "iOS"
            ], merge: true) { err in
                if let err = err {
                    print("Error saving latest token: \(err)")
                } else {
                    UserDefaults.standard.setValue(token, forKey: token + "_" + Auth.auth().currentUser!.uid)
                }
            }
            }
    

    
}



extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
      withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
      let userInfo = notification.request.content.userInfo
        print("did receive")

      Messaging.messaging().appDidReceiveMessage(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("did receive")
      Messaging.messaging().appDidReceiveMessage(userInfo)

      completionHandler()
    }

}


exports.createdLostPetAlerts = functions.firestore
    .document("Lost/{lostPetId}")
    .onCreate((snap, context) => {


      const registrationToken = "FCM_TOKEN_HERE";



      //This works.

      // const message = {
      //   data: {
      //     score: '850',
      //     time: '2:45'
      //   },
      //   token: registrationToken
      // };
      
      // // Send a message to the device corresponding to the provided
      // // registration token.
      // admin.messaging().send(message)
      //   .then((response) => {
      //     // Response is a message ID string.
      //     console.log('Successfully sent message:', response);
      //   })
      //   .catch((error) => {
      //     console.log('Error sending message:', error);
      //   });


      //This does not work.

      const payloadIos = {
        "data" : {
          "lostPetId" : "12345",
          "lostPetName": "Fido",
          "messageType": "1"
        },
        "token": registrationToken
      }

        admin.messaging().sendToDevice(registrationToken, payloadIos)
        .then((response) => {
          // Response is a message ID string.
          console.log('Successfully sent message:', response);
        })
        .catch((error) => {
          console.log('Error sending message:', error);
        });
}



标签: iosfirebasegoogle-cloud-functionsfirebase-cloud-messaging

解决方案


事实证明,iOS 要求您传递标题和正文,否则它不会显示通知。像这样的东西有效。

          const payload = {
              "notification":{
                "title": "Needed title",
                "body": "Needed body"
              },
              "data" : {
              "lostPetId" : context.params.lostPetId,
              "lostPetName": lostPet.name,
              "messageType": "1"
              }
          }

推荐阅读