【问题标题】:Can't get FCM push notifications from my server on iOS (working on Android)无法从我在 iOS 上的服务器获取 FCM 推送通知(在 Android 上工作)
【发布时间】:2020-01-08 09:10:31
【问题描述】:

我是 iOS/Swift 新手,无法让推送通知正常工作。我已将服务器后端配置为在发生某些操作时将通知推送到我的应用程序。我已经通过 FCM 配置了数据通知,因为我需要通知中的一些自定义数据来打开一个活动/视图或另一个。这是用于发送通知的代码(python/django):

registration_id = profiletarget.device_token
    message_title = "Ha llegado tu turno"
    message_body = "Entra y escribe en el relato para el que estás en cola"
    data_message = {
        "title" : "¿Listo para escribir?",
        "body" : "Ha llegado tu turno para escribir en el relato. Recuerda que tienes un minuto para aceptar tu turno y 3 para escribir.",
        "bookid" : booktarget.pk,
        "multimediaurl" : multimediaused.url
        }
    result = push_service.notify_single_device(registration_id=registration_id, data_message=data_message)

此代码中的所有内容都正常工作,因为我在 Android 上正确获取了它们。但在 iOS 上...我无法正常工作。

我已获得通知令牌,已将其发布到我的服务器,我已使用 FCM 控制台上的通知发送器发送测试推送通知(iPhone 获取它们),但不是来自我的自定义方法的通知,它没有什么都不显示。服务器方法有什么问题还是我遗漏了什么?

这是快速代码:

import UIKit
import KeychainSwift
import Firebase
import FirebaseMessaging

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

    var window : UIWindow?;
    var storyboard : UIStoryboard?;
    var token = ""
    let gcmMessageIDKey = "gcm.message_id"


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        self.storyboard = UIStoryboard(name: "Main", bundle: Bundle.main);
        UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont(name: "PT Sans", size: 12)!], for: .normal)
        UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont(name: "PT sans", size: 12)!], for: .selected)
        let keychain = KeychainSwift()
        token = keychain.get("token") ?? ""
        if (token != ""){
            print("log-token: ", token)
            print("log-redirection: ", "no redirection needed!")
            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()

        } else {
            print("log-token: ", "noToken")
            print("log-redirection: ", "redirection to LoginController")
            window?.rootViewController = self.storyboard?.instantiateViewController(withIdentifier: "loginView");
        }
        return true
    }

    func application(
      _ application: UIApplication,
      didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
    ) {
      let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
      let token = tokenParts.joined()
      print("Device Token: \(token)")
        postNotificationToken(token: token)
    }

    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)
      // 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 postNotificationToken(token:String) {
        var request = URLRequest(url: URL(string: "https://myurl?myparam="+token)!)
        request.httpMethod = "GET"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("Bearer "+self.token, forHTTPHeaderField: "myauth")

        let session = URLSession.shared
        let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
            let httpURLResponse = response as? HTTPURLResponse;
            if (httpURLResponse?.statusCode == 200){
                let string = String.init(data: data!, encoding: String.Encoding.utf8)
                print(string)
            } else {
                print(httpURLResponse?.statusCode)
            }
        })

        task.resume()
    }



    func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
        print("Received data message: \(remoteMessage.appData)")
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        completionHandler([.alert, .badge, .sound])
    }


    // MARK: UISceneSession Lifecycle


}

这是我的目标能力:

这是我的开发者帐户中用于 APN 通知的密钥:

使用 FCM 控制台,通知正常:

感谢您阅读这篇长文。任何事情都会有帮助!

【问题讨论】:

  • 好吧,您的方法已命名为func postNotificationToken(token:String),并且您写道“我已将其发布到我的服务器”,但我看到了request.httpMethod = "GET"。这让我很困扰。
  • 是的,我写“post”不是http方法,我只是说它是send的同义词。令牌通过 get 请求作为 url 中的参数发送到服务器。并且令牌已正确存储在数据库中(我已检查它在 xcode 打印和数据库中是否相同)
  • 是的,GoogleService-info.plist 在项目中,并且推送通知已添加到功能中。正如我在帖子中所说,来自 FCM 控制台的默认测试通知可以正常工作(所以它会接收它)......不起作用的是我的自定义通知
  • 您创建的推送通知证书可能有问题。或者当您将该证书添加到 FCM 中的项目时可能出现问题。建议通过直接从 FCM 控制台发送通知来调试/测试它。如果这确实有效,那么问题出在您的服务器代码上,否则重新创建推送 cer。并再次将其添加到 FCM 项目中。
  • 您好,谢谢。正如我所说,设置 FCM 控制台通知工作正常,但我无法让它与我的自定义通知一起工作(适用于 android),使用自定义通知在 iphone 上没有收到任何内容,但 FCM 控制台一切正常。

标签: python ios swift django firebase-cloud-messaging


【解决方案1】:

好的,我已经将其他帖子中的一些答案和一些代码混合在一起了。首先,我使用的是 APN 密钥,而不是 APN 证书。

其次,我正在检查用户必须发送通知(iOS/Android)的操作系统,以便配置不同的通知结构。这是 python/django 中的通知系统,使用 PyFCM 库并将 iOS 通知作为警报发送,正如我在 post 上找到的那样:

if devicetype == "and":
        registration_id = profiletarget.device_token
        message_title = "default-title"
        message_body = "default-body"
        data_message = {
            "title" : "title",
            "body" : "body",
            "bookid" : booktarget.pk,
            "multimediaurl" : multimediaused.url
            }
        result = push_service.notify_single_device(registration_id=registration_id, data_message=data_message)
    else:
        registration_id = profiletarget.device_token
        message_title = "title"
        message_body = "body"
        data_message = {
            "bookid" : booktarget.pk,
            "multimediaurl" : multimediaused.url,
            }
        result = push_service.notify_single_device(registration_id=registration_id,
                                   message_title=message_title,
                                   message_body=message_body,
                                   data_message=data_message,
                                   extra_kwargs={"apns_push_type": "alert"}
                                   )

在 Xcode 中,我只需要添加推送通知和后台模式的功能 - 远程通知,如问题图片中所示。

在代码中,我错过了与此方法对应的部分 firebase 教程:

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["body"] {
        print("Notif metod 1")
//        gotoWritting(bookid: messageID)
      }

      // Print full message.
      print("Notif metod 1")
    }

    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["body"]{
        print("Notif metod 2")
//        gotoWritting(bookid: messageID)
      }

      // Print full message.
      print(userInfo["bookid"]!)
        gotoWritting(bookid: Int(userInfo["bookid"]! as! String) ?? 90)
      completionHandler(UIBackgroundFetchResult.newData)
    }

第二个是由通知点击触发的(背景、前景和应用程序关闭...),一旦点击,我可以使用一些通知参数重定向用户。

我无法在以前的版本上进行测试,但希望它可以在 iOS 9 上运行(如果你知道这是否可行,请告诉我)。

感谢所有帮助过的人!

【讨论】:

    【解决方案2】:

    您是否为应用证书配置了推送通知? ?

    您还需要在 Xcode 中配置项目,选择您的目标之一,然后转到 -> 登录和功能添加 推送通知 每个目标的能力,推送在模拟器中不可见,但是触发了 didReceiveRemoteNotification 方法,如果您收到它们,您可以使用一些日志或断点进行调试。

    【讨论】:

    • 我做了,会上传图片。调试控制台从通知中向我显示了这条消息:[Firebase/Messaging][I-FCM012002] Error in application:didFailToRegisterForRemoteNotificationsWithError: remote notifications are not supported in the simulator 所以我什至无法触发它。将使用功能图片进行编辑。
    • 当我的服务器发送通知时,它在控制台上什么也没有显示...另外请注意,我有两个应该获取注册令牌的函数...但是两个响应是不同的。第一个 64 个字符,第二个 152 个……女巫一个是对的吗?我都试过了...... FCM 控制台工作了,但我没有在那里上传令牌,所以也许有问题(我发送了一个错误的令牌......)
    • 好吧,我知道你用你的照片在说什么了。证书应该为0吗?还是让我创建一个并将其放在某个地方?
    • 你必须在你自己的服务器上创建你的 SSL 证书,我一直用 Firebase Analytics 配置它,所以不需要 afaik。
    • apn 键不能用? FCM 给了我两个选择...并说用 apn 键更好
    【解决方案3】:

    尝试使用自定义负载发送这种类型的自定义格式

    注意:这是 node.js 代码

        let message = {
        tokens: iosTokens,
        notification: {
            title: "title",
            body: "",
        },data = {
        "title" : "¿Listo para escribir?",
        "body" : "Ha llegado tu turno para escribir en el relato. Recuerda que tienes un minuto para aceptar tu turno y 3 para escribir.",
        "bookid" : booktarget.pk
        },apns : {
            payload: {
                aps: {
                    mutableContent: true,
                    contentAvailable: true,
                    category: "CustomSamplePush",
                    alert: {
                        launchImage: "imageURL.jpeg",
                        sound: "default"
                    }
                }
    
            }
        }
    }; 
    

    【讨论】:

    • 是的...问题是使用 python fcm 库方法重新创建该结构...该 apns 有效负载应该替换我实际的data-message
    • 谢谢,会试试的。现在和发布模拟器控制台调试
    • 是的,这就是我之前所说的关于在 python 中重新创建的内容。会尝试。谢谢!
    猜你喜欢
    • 2019-02-20
    • 2012-02-17
    • 2020-09-14
    • 1970-01-01
    • 2018-03-05
    • 1970-01-01
    • 1970-01-01
    • 2017-07-06
    • 2016-09-27
    相关资源
    最近更新 更多