Las notificaciones push de Back4App a través de Parse Server no funcionan para iOS

Actualización 2:

Sigo lidiando con el mismo problema, pero me di cuenta de que mi enfoque podría ser defectuoso: que no puedo enviar notificaciones automáticas desde Parse Server a través de Firebase Cloud Messaging a dispositivos iOS de la misma manera que lo hago para los dispositivos Android.

En cambio, por lo que entendí, el servidor Parse enviará una notificación de inserción a través de Firebase (si el dispositivo receptor es un dispositivo Android) O a través del Servicio de notificación de inserción de Apple (si el dispositivo receptor es un dispositivo iOS) en función de la instalación suscrito al canal de destino (corríjame si me equivoco).

¿Qué campos en el objeto de instalación son necesarios para que Parse Server envíe notificaciones automáticas a los dispositivos iOS? ¿O qué valores deben guardarse para GCMSenderId y pushType para los objetos de instalación creados en dispositivos iOS?

Actualización I:

Las notificaciones push a iOS desde ParseServer a través de Back4App aún no funcionan.

Intenté enviar notificaciones a través de una función en la nube para evitar el panel de notificaciones de Back4App e implementé una función muy rudimentaria en mi main.js:

Parse.Cloud.define('pushsample', async () => {
       
       Parse.Push.send({
         channels: ["TestChannelForMyiOSDevice"],
            data: {
                title: "Hello from Cloud Code",
                alert: "It finally worked!",
            }
        }, { useMasterKey: true });
      
      return 'pushsample called successfully';
});

Estoy devolviendo una cadena porque simplemente copié una estructura de plantilla del área de juegos de ParseSwift que tiene una cadena de tipo de retorno, ya que esto fue pensado como una prueba rápida y sucia. Todavía no descubrí cómo devolver una respuesta adecuada del servidor con el manejo de errores.

La ParseCloudestructura correspondiente:

struct CloudFunction: ParseCloud {

    //: Return type of your Cloud Function
    typealias ReturnType = String

    //: These are required by `ParseCloud`
    var functionJobName: String

}

Cuando llamo a esta función, el servidor Back4App responde con 'pushsample llamado con éxito' y la notificación push también aparece en la lista Back4App Past Pushes. Sin embargo, solo aparece allí y no se envía a mi dispositivo iOS físico.

No tengo experiencia con JavaScript hasta ahora, así que si alguien puede decirme cómo puedo solucionar esto mejor u obtener más información de diagnóstico del servidor, eso ya sería de gran ayuda.

Como se indicó en la publicación original, utilizo Firebase Cloud Messaging y aprovecho la propiedad Channel guardada en los objetos Parse Installation en mi servidor para dirigirme a usuarios específicos. Esto funciona perfectamente para la versión de Android de la aplicación, también cuando se usa Cloud Code.

Para iOS, solo puedo enviar notificaciones automáticas desde Firebase directamente a través de la página "Envíe su primer mensaje", dirigidas a mi dispositivo a través de su token de Mensajería. Enviar una notificación automática a través de los canales de Back4App a través de Firebase no funciona.

¿Cómo puedo arreglar esto?

¡Cualquier entrada es bienvenida!

Estoy usando Parse Server 4.2.0 y ParseSwift 2.5.1 en mi aplicación.

 


Publicación original:

Tengo una aplicación de Android que usa Back4App como backend y actualmente la estoy transfiriendo a iOS. He tenido problemas para enviar notificaciones automáticas a la aplicación de iOS a través del Panel de control de Parse (o a través de mi backend de Parse en general).

En mi aplicación de Android, implementé notificaciones automáticas siguiendo las guías de Back4App usando Firebase Cloud Messaging, que funcionó perfectamente desde el primer momento. Implementé Firebase, me registré para notificaciones automáticas y guardé el GCMSenderId y el token de dispositivo en mi instalación de Parse y registré Firebase en la configuración del servidor de notificaciones automáticas de Android en Back4App y puedo enviar notificaciones automáticas sin ningún problema.

Para la versión de iOS intenté emular esto. Creé el archivo de clave APN .p8, lo cargué en Firebase, implementé Firebase en mi App Delegate, me registré para recibir notificaciones push, agregué capacidades de notificación push en la configuración de la aplicación, guardé el GCMSenderId y el deviceToken en mi instalación de Parse, y también cargué el Archivo APN a la configuración del servidor de notificaciones push de iOS en Back4App.

Con esta configuración, puedo:

  • enviar una notificación push desde Firebase Cloud Messaging directamente a mi dispositivo Android con un token de dispositivo dado
  • enviar una notificación automática desde Firebase Cloud Messaging directamente a mi dispositivo iOS con un token de dispositivo determinado
  • enviar una notificación automática desde ParseDashboard a mi dispositivo Android (por ejemplo, según el canal suscrito en la instalación)
  • no puedo enviar una notificación automática desde ParseDashboard a mi dispositivo iOS

Mientras se envían las notificaciones automáticas (según ParseDashboard), no se envían a mi dispositivo iOS. Quiero enviar las inserciones a través de Parse para poder usar el mismo sistema de distribución basado en canales que ya está implementado con la aplicación de Android.

Supuse que se trataba de un problema en algún lugar dentro de mi backend de Back4App, así que intenté configurar el valor de "producción" en la parte de la clave APN Auth de la configuración push de iOS de Back4App en verdadero y falso, pero ambas formas no funcionan.

¿Cómo puedo arreglar esto?

Comencé a programar en iOS recientemente y hasta ahora he estado usando SwiftUI para la aplicación, por lo que no estoy muy familiarizado con los eventos del ciclo de vida del delegado de la aplicación, pero pude hacerlo funcionar hasta este punto. Esto es lo que estoy usando actualmente que funciona en los escenarios enumerados anteriormente:

class AppDelegate: NSObject, UIApplicationDelegate {
    let gcmMessageIDKey = "gcm.message_id"

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> 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 let messageID = userInfo[gcmMessageIDKey] {
        print("Message ID: \(messageID)")
      }

      print(userInfo)

      completionHandler(UIBackgroundFetchResult.newData)
    }
    
}

@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

    if let messageID = userInfo[gcmMessageIDKey] {
        print("Message ID: \(messageID)")
    }

    print(userInfo)

    completionHandler([[.banner, .badge, .sound]])
  }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        
        print("didRegisterForRemoteNotificationsWithDeviceToken: token: \(deviceToken)")
    }

    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        
        print("application:didFailToRegisterForRemoteNotificationsWithError: %@", error)
        
    }

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo

    if let messageID = userInfo[gcmMessageIDKey] {
      print("Message ID from userNotificationCenter didReceive: \(messageID)")
    }

    print(userInfo)

    completionHandler()
  }
}

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {

        guard let deviceTokenFirebase = fcmToken else { return }
        
        var currentInstallation = Installation.current
        currentInstallation?.pushType = "gcm"
        currentInstallation?.GCMSenderId = "mySenderId"
        currentInstallation?.deviceToken = deviceTokenFirebase
        currentInstallation?.save { results in
            
            switch results {
            case .success(let savedInstallation):
                print("Successfully save installation: \(savedInstallation)")
            case .failure(let error):
                print("Failed to update installation: \(error)")
            }
        }  
        
    }
}

¡Cualquier ayuda es muy apreciada!

Answer

Lo que se guardó fue el token de dispositivo falso: ¡era el token de Firebase, no el token de APN!

Así es como lo descubrí:

Pude enviar notificaciones automáticas desde Firebase, pero no desde ParseServer. El token que había guardado era el token de la messagingfunción en my App Delegate, que es una función de Firebase, no una función de APN. Me di cuenta de que ese token nunca cambió, mientras leía en línea que una aplicación se vuelve a registrar para el Servicio APN y, por lo tanto, obtiene un nuevo token APN cada vez que se inicia la aplicación.

Al revisar mi registro, descubrí que nunca llamaron a ninguno didRegisterForRemoteNotificationsWithDeviceTokende los dos didFailToRegisterForRemoteNotificationsWithErrorpara mi aplicación.

¿Qué resolvió el problema? Comenté las partes de Firebase en mi didFinishLaunchingWithOptions:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> 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: { (granted, error) in
                if let error = error {
                    print("requestAuthorization error: \(error)")
                } else if granted {
                    DispatchQueue.main.async {
                        print("granted: now registeringForRemoteNotifications()")
                        UIApplication.shared.registerForRemoteNotifications()
                    }
                }
            })
        } else {
          let settings: UIUserNotificationSettings =
          UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
          application.registerUserNotificationSettings(settings)
        }

        return true
    }

Me imagino que Firebase de alguna manera evita que se llamen las funciones APN. Como ya no necesitaré Firebase Cloud Messaging, solo usaré Firebase Analytics y enviaré mis notificaciones automáticas desde mi servidor Parse.