简体   繁体   中英

Can't get FCM push notifications from my server on iOS (working on Android)

I'm new to iOS/Swift and can't get push notifications working. I have configured my server backend to push notifications to my app when some action happens. I have configured a data notification trough FCM because I need some custom data in the notification to open one activity/view or another. This is the code used to send the notification (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)

Everything inside this code is working, because I get them correctly on Android. But on iOS... I can't get it working.

I have get the notifications token, I have post it to my server, I have use the notification sender on FCM console to send test push notifications (the iPhone get them), but not the ones from my custom method, It doesn't show anything. Is anything wrong in the server method or am I missing something?

This is the swift code:

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


}

This are my target capabilities:

项目目标能力

And this is my key for APN notifications in my developer account:

APN 密钥

With FCM console, notifications are Ok:

FCM 控制台

Thanks for reading this long post. Anything will help!

Ok, I have it working mixing some of the answers and some code found on other posts. First of all, I am using APN key, not APN certificates.

Second, I'm checking what OS has the user to I have to send the notification (iOS/Android) so I can configure different notification structure. This is the notification system in python/django using PyFCM library and sending the iOS notification as alert, as I found on this 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"}
                                   )

In Xcode I only had to ad capabilities for push notifications and background mode - remote notifications, as posted in the question pic.

In code, I missed some part of firebase tutorial corresponding to this methods:

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

The second one is the one is getting triggered by the notification click (background, foreground and app closed...) and once its clicked, I can redirect the user with some notification params.

I cant test on previous version, but hope it works on iOS 9 to (If you know if that works or not, let me know please).

Thanks to everyone who helped!

Did you configured your app certificate with push notifications? 👇

iOS 推送通知

Also you will need to configure the project in Xcode select one of your targets and go to -> SignIn and capabilities and add the Push Notifications capability for each target, the pushes ain't working in the simulator visible, but the method didReceiveRemoteNotification is triggered, you can debug with some log or breakpoint if you are receiving them.

根据此更改信息 plist

Try to send this type of custom formate with custom payload

note : this is node.js code

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

        }
    }
}; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM