Detecta si la aplicación se inició / abrió desde una notificación push

172

¿Es posible saber si la aplicación se inició / abrió desde una notificación push?

Supongo que el evento de lanzamiento se puede ver aquí:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    if (launchOptions != nil) {
         // Launched from push notification
         NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    }
}

Sin embargo, ¿cómo puedo detectar que se abrió desde una notificación push cuando la aplicación estaba en segundo plano?

joao
fuente
66
Esta es una publicación antigua pero muy útil. Lamentablemente, las respuestas principales no resuelven el problema (como indican los comentarios). Considere marcar una nueva respuesta como 'aceptada' ya que la actual no está completa.
MobileVet
1
Esta pregunta tiene más de 100k vistas, pero la respuesta seleccionada es incorrecta o completa. Para los visitantes, considere ordenar por Activo en lugar de por Votos para encontrar soluciones modernas.
Albert Renshaw

Respuestas:

187

Ver este código:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground  )
    {
         //opened from a push notification when the app was on background
    }
}

igual que

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
shanegao
fuente
19
@ManuelM. Esta es una buena respuesta, ya que muestra cómo detectar cuándo una aplicación en segundo plano se coloca en primer plano desde una notificación push. Para cuando la aplicación no se está ejecutando, necesita la respuesta de M.Othman a continuación.
OpenUserX03
66
Recibo la llamada a la aplicación: didReceiveRemoteNotification: después de tocar la notificación, independientemente de si la aplicación está en segundo plano o no se está ejecutando, por lo que esta respuesta se adapta perfectamente a mis necesidades. Probado en iOS 7 y 8
Newtz
16
Como algunos otros señalaron, esto no detecta "lanzado / abierto desde una notificación push". Esto se llama cuando se recibe la notificación, no cuando se abre. Entonces, si recibió una notificación en el BG pero tocó el ícono de la aplicación para abrir la aplicación, el código que tiene aquí todavía se ejecutará, y es posible que abra una página que el usuario no pretendía abrir.
Bao Lei
44
@ManuelM. este método no indica si la aplicación se abrió a través del centro de notificaciones frente al ícono de la aplicación si los modos de fondo están activados: la notificación remota está marcada. Lo hace cuando no está marcado. He documentado la diferencia en esta publicación: stackoverflow.com/questions/32061897/…
Bao Lei
2
Confirmó que esto funciona con Google Cloud Messaging.
CularBytes
127

tarde pero tal vez útil

Cuando la aplicación no se está ejecutando

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

se llama ..

donde necesita verificar la notificación push

NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
    NSLog(@"app recieved notification from remote%@",notification);
    [self application:application didReceiveRemoteNotification:notification];
} else {
    NSLog(@"app did not recieve notification");
}
M.Othman
fuente
2
Tenga en cuenta que en el fragmento anterior, la notificación no debe declararse como (UILocalNotification *) sino como (NSDictionary *)
cosmix
1
De esta manera, puede ver si hubo notificaciones para la aplicación, ¡mientras no se está ejecutando! La pregunta era cómo detectar si la aplicación se abrió desde una notificación. En este caso, se llama a didReceiveRemoteNotification, incluso si la aplicación no se estaba ejecutando en absoluto. - Me gusta su respuesta, porque es bastante importante para muchos casos, pero no es la respuesta correcta a la pregunta.
Axel Zehden
¿Su respuesta y esta respuesta están haciendo lo mismo?
Miel
38

El problema que tuvimos fue la actualización correcta de la vista después de iniciar la aplicación. Aquí hay secuencias complicadas de métodos de ciclo de vida que se vuelven confusas.

Métodos de ciclo de vida

Nuestras pruebas para iOS 10 revelaron las siguientes secuencias de métodos de ciclo de vida para varios casos:

DELEGATE METHODS CALLED WHEN OPENING APP  

Opening app when system killed or user killed  
    didFinishLaunchingWithOptions  
    applicationDidBecomeActive    

Opening app when backgrounded  
    applicationWillEnterForeground  
    applicationDidBecomeActive  

DELEGATE METHODS WHEN OPENING PUSH

Opening push when system killed
    [receiving push causes didFinishLaunchingWithOptions (with options) and didReceiveRemoteNotification:background]
    applicationWillEnterForeground
    didReceiveRemoteNotification:inactive
    applicationDidBecomeActive

Opening push when user killed
    didFinishLaunchingWithOptions (with options)
    didReceiveRemoteNotification:inactive [only completionHandler version]
    applicationDidBecomeActive

Opening push when backgrounded
    [receiving push causes didReceiveRemoteNotification:background]
    applicationWillEnterForeground
    didReceiveRemoteNotification:inactive
    applicationDidBecomeActive

El problema

Ok, ahora necesitamos:

  1. Determine si el usuario está abriendo la aplicación desde un momento
  2. Actualice la vista según el estado de inserción
  3. Borre el estado para que las aperturas posteriores no devuelvan al usuario a la misma posición.

Lo complicado es que la actualización de la vista tiene que suceder cuando la aplicación realmente se activa, que es el mismo método de ciclo de vida en todos los casos.

Bosquejo de nuestra solución.

Estos son los principales componentes de nuestra solución:

  1. Almacene una notificationUserInfovariable de instancia en AppDelegate.
  2. Ubicado notificationUserInfo = nilen ambos applicationWillEnterForegroundy didFinishLaunchingWithOptions.
  3. Establecer notificationUserInfo = userInfoendidReceiveRemoteNotification:inactive
  4. Desde applicationDidBecomeActivesiempre llame a un método personalizado openViewFromNotificationy pase self.notificationUserInfo. Si self.notificationUserInfoes nulo, regrese temprano, de lo contrario abra la vista en función del estado de notificación que se encuentra en self.notificationUserInfo.

Explicación

Cuando se abre desde una inserción didFinishLaunchingWithOptionso applicationWillEnterForegroundsiempre se llama inmediatamente antes didReceiveRemoteNotification:inactive, por lo que primero restablece NoticeIserInfo en estos métodos para que no haya un estado obsoleto. Luego, si didReceiveRemoteNotification:inactivese llama, sabemos que estamos abriendo desde un empuje, por lo que configuramos el self.notificationUserInfoque luego se recoge applicationDidBecomeActivepara reenviar al usuario a la vista correcta.

Hay un caso final que es si el usuario tiene la aplicación abierta dentro del selector de aplicaciones (es decir, tocando dos veces el botón de inicio mientras la aplicación está en primer plano) y luego recibe una notificación push. En este caso solo didReceiveRemoteNotification:inactivese llama, y ​​ni WillEnterForeground niFinishLaunching son llamados, por lo que necesita un estado especial para manejar ese caso.

Espero que esto ayude.

Eric Conner
fuente
Finalmente algo que funciona, gracias! Quería crear un indicador "appResuming" y abrir la pantalla en los receivemétodos cuando el estado de la aplicación está activo o la aplicación se reanuda. Eso podría generar problemas al cambiar los VC cuando la aplicación aún está inactiva. Su solución se ve genial, hasta que Apple cambie el ciclo de vida nuevamente.
shelll
¿Qué pasa con iOS 9? ¿Se llaman los métodos del ciclo de vida de la misma manera y orden? Ya no tengo dispositivos iOS 9, así que no puedo probar esto correctamente.
shelll
2
Hay dos casos más, excepto el conmutador de aplicaciones. 1) Cuando el centro de notificaciones se extrae de la parte superior y se superpone a la aplicación 2) Cuando el panel de iOS con wifi / BT / etc. se extrae de la parte inferior y se superpone a la aplicación. En los tres casos solo applicationWillResignActivese llama al y luego al applicationDidBecomeActive. Por lo tanto, después de la applicationWillResignActivellamada, no guarde la notificación recibida hasta que se llame a applicationDidEnterBackgroundo applicationDidBecomeActive.
shelll
Gracias por agregar estos casos @shelll. ¡Siempre se vuelve más complicado! No estoy seguro acerca de iOS9. Diría que es seguro asumir que son lo mismo, pero quién sabe.
Eric Conner
Solo un aviso. Estaba probando iOS 11 Beta 9 hoy y descubrí que en el caso de que tenga su aplicación en primer plano, bloquee el teléfono y luego seleccione una notificación de inserción desde la pantalla de bloqueo, está llamando a didReceiveRemoteNotification: fondo justo antes de llamar a applicationWillEnterForeground en lugar de lo que estamos viendo en iOS 10, donde llama a applicationWillEnterForeground y luego hizoReceiveRemoteNotification: inactivo, por lo que este es un caso extremo que aún no está cubierto. En mi opinión, esto es un error en el código de iOS, pero dado lo cerca que está la versión de iOS 11, es algo a tener en cuenta.
Roy
24

Esta es una publicación muy desgastada ... pero todavía le falta una solución real al problema (como se señala en los diversos comentarios).

La pregunta original es acerca de detectar cuándo se inició / abrió la aplicación desde una notificación push, por ejemplo , un usuario toca la notificación. Ninguna de las respuestas cubre este caso.

La razón se puede ver en el flujo de llamadas cuando llega una notificación, application:didReceiveRemoteNotification...

recibe una llamada cuando se recibe la notificación Y nuevamente cuando el usuario toca la notificación. Debido a esto, no se puede saber con solo mirar UIApplicationStatesi el usuario lo tocó.

Además, ya no necesita manejar la situación de un "arranque en frío" de la aplicación application:didFinishLaunchingWithOptions...como application:didReceiveRemoteNotification...se llama nuevamente después del lanzamiento en iOS 9+ (quizás 8 también).

Entonces, ¿cómo puede saber si el toque del usuario inició la cadena de eventos? Mi solución es marcar la hora a la que la aplicación comienza a salir del fondo o iniciar en frío y luego verificar esa hora application:didReceiveRemoteNotification.... Si es inferior a 0.1s, puede estar seguro de que el toque activó el inicio.

Swift 2.x

class AppDelegate: UIResponder, UIApplicationDelegate {

  var wakeTime : NSDate = NSDate()        // when did our application wake up most recently?

  func applicationWillEnterForeground(application: UIApplication) {    
    // time stamp the entering of foreground so we can tell how we got here
    wakeTime = NSDate()
  }

  func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    // ensure the userInfo dictionary has the data you expect
    if let type = userInfo["type"] as? String where type == "status" {
      // IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
      if application.applicationState != UIApplicationState.Background && NSDate().timeIntervalSinceDate(wakeTime) < 0.1 {
        // User Tap on notification Started the App
      }
      else {
        // DO stuff here if you ONLY want it to happen when the push arrives
      }
      completionHandler(.NewData)
    }
    else {
      completionHandler(.NoData)
    }
  }
}

Swift 3

class AppDelegate: UIResponder, UIApplicationDelegate {

    var wakeTime : Date = Date()        // when did our application wake up most recently?

    func applicationWillEnterForeground(_ application: UIApplication) {
      // time stamp the entering of foreground so we can tell how we got here
      wakeTime = Date()
    }

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

      // ensure the userInfo dictionary has the data you expect
      if let type = userInfo["type"] as? String, type == "status" {
        // IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
        if application.applicationState != UIApplicationState.background && Date().timeIntervalSince(wakeTime) < 0.1 {
          // User Tap on notification Started the App
        }
        else {
          // DO stuff here if you ONLY want it to happen when the push arrives
        }
        completionHandler(.newData)
      }
      else {
        completionHandler(.noData)
      }
    }
}

He probado esto para ambos casos (aplicación en segundo plano, aplicación no se ejecuta) en iOS 9+ y funciona de maravilla. 0.1s también es bastante conservador, el valor real es ~ 0.002s, por lo que 0.01 también está bien.

MobileVet
fuente
1
Esta parece ser la única solución de trabajo que diferencia entre tocar la notificación y tener la barra de estado abierta sobre la aplicación.
liviucmg
44
Esta es la única solución de trabajo de todo el StackOverflow. Lo único que me gustaría agregar, es que cuando es compatible con iOS 10 y superior, simplemente puede usar la UNNotificationCenterAPI, específicamente los métodos UNNotificationCenterDelegate. Esos userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) métodos de función de llamada API solo cuando el usuario ha hecho tapping en la notificación.
DenHeadless
¿Cómo se ve Swift 3?
Jochen Österreicher
La solución no funciona mientras una aplicación está en estado inactivo (el usuario desliza hacia abajo el centro de notificaciones o desliza hacia arriba el centro de control) y recibe una notificación. Cuando el usuario toca la notificación, la aplicación no recibe la applicationWillEnterForeground llamada, como resultado, la solución no puede detectar el toque.
DevGansta
@DevGansta Cuando agregue su clase como UNUserNotificationCenter.current().delegateen application:didFinishLaunchingWithOptions, la aplicación llamará userNotificationCenter(didReceive response)después del toque en el caso que describió
Dorian Roy
22

Cuando finaliza la aplicación y el usuario toca la notificación push

public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
   if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
      print("from push")
    }
}

Cuando la aplicación está en segundo plano y el usuario toca la notificación push

Si el usuario abre su aplicación desde la alerta que se muestra en el sistema, el sistema puede volver a llamar a este método cuando su aplicación esté a punto de entrar en primer plano para que pueda actualizar su interfaz de usuario y mostrar información relacionada con la notificación.

public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
  if application.applicationState == .inactive {
    print("from push")
  }
}

Dependiendo de su aplicación, también puede enviarle un empuje silencioso con el content-availableinterior aps, así que tenga en cuenta esto también :) Consulte https://stackoverflow.com/a/33778990/1418457

onmyway133
fuente
2
Solo responde que no se siente como un truco sucio y correcto. Lo que me falta es si la aplicación está en segundo plano y el usuario la abre manualmente, ¿cómo verificar eso? Al mismo tiempo que puede verificar el empuje, arranque en frío y empuje desde el fondo.
Jochen Österreicher
1
@ JochenÖsterreicher Hola, lo resumo aquí, consulte medium.com/@onmyway133/…
onmyway133
19

Swift 2.0 para estado 'No en ejecución' (Notificación local y remota)

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


// Handle notification
if (launchOptions != nil) {

    // For local Notification
    if let localNotificationInfo = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {

        if let something = localNotificationInfo.userInfo!["yourKey"] as? String {
            self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
        }


    } else

    // For remote Notification
    if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as! [NSObject : AnyObject]? {

        if let something = remoteNotification["yourKey"] as? String {
            self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
        }
    }

}


return true
}
Włodzimierz Woźniak
fuente
15

En application:didReceiveRemoteNotification:comprobación de si ha recibido la notificación cuando su aplicación está en el primer o segundo plano.

Si se recibió en segundo plano, inicie la aplicación desde la notificación.

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        NSLog(@"Notification received by running app");
    } else {
        NSLog(@"App opened from Notification");
    }
}
Madhu
fuente
3
Tenga en cuenta que "Aplicación abierta desde notificación" será un falso positivo si la notificación se envía mientras el usuario está en una pantalla diferente (por ejemplo, si abren la barra de estado y luego reciben una notificación de su aplicación).
Kevin Cooper
44
@Kevin Exactamente. Hace que te preguntes por qué Apple aparentemente puso un interno para diseñar el proceso de manejo de notificaciones ...
Andreas
¿Cómo podemos detectar si tocamos en la notificación recibida en estado activo
Mayank Jain
13

Para rápido:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    PFPush.handlePush(userInfo)

    if application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background {
        //opened from a push notification when the app was in the background

    }

}
LondresGuy
fuente
4

Sí, puede detectar por este método en appDelegate :

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
      /* your Code*/
}

Para notificaciones locales:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}
Imposible
fuente
1
Este método no se llama si la aplicación no se está ejecutando. Eso es lo que pedimos aquí
Pfitz
Mi problema no es manejar la notificación, sino saber si se abrió al hacer clic en el banner (cuando la aplicación está en segundo plano).
joao
3

si alguien quiere la respuesta en Swift 3

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    switch application.applicationState {
    case .active:
        //app is currently active, can update badges count here
        break
    case .inactive:
        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here
        break
    case .background:
        //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
        break
    default:
        break
    }
}
Hamid Shahsavari
fuente
pero ¿cómo saber si la aplicación se abre tocando notificación de inserción cuando la aplicación se termina
user3804063
1
cuando alguien toca el botón, la aplicación se abrirá, sin importar si fue cancelada o no. y el caso .inactive está llamando
Hamid Shahsavari
1
i necesidad de detectar si la aplicación se abre con cinta adhesiva el empuje y quiere desplazarse a instagram sierra respectivos contenidos i hacer eso
user3804063
¿Qué hay de las notificaciones locales?
Amir Shabani
3

Publicar esto para los usuarios de Xamarin.

La clave para detectar si la aplicación se inició mediante una notificación push es el AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)método y el diccionario de opciones que se pasa.

El diccionario opciones tendrá esta clave en ella si se trata de una notificación locales: UIApplication.LaunchOptionsLocalNotificationKey.

Si es una notificación remota, lo será UIApplication.LaunchOptionsRemoteNotificationKey.

Cuando la clave es LaunchOptionsLocalNotificationKey, el objeto es de tipo UILocalNotification. Luego puede ver la notificación y determinar qué notificación específica es.

Pro-tip: UILocalNotificationno tiene un identificador, de la misma manera UNNotificationRequest. Ponga una clave de diccionario en la información de usuario que contenga un requestId para que al probarlo UILocalNotification, tenga un requestId específico disponible para basar algo de lógica.

Descubrí que incluso en dispositivos iOS 10+ que al crear notificaciones de ubicación usando UNUserNotificationCenter's AddNotificationRequest& UNMutableNotificationContent, que cuando la aplicación no se está ejecutando (la eliminé), y se inicia tocando la notificación en el centro de notificaciones, que el diccionario todavía contiene El UILocalNotificaitonobjeto.

Esto significa que mi código que verifica el lanzamiento basado en notificaciones funcionará en dispositivos iOS8 e iOS 10+

public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
    _logger.InfoFormat("FinishedLaunching");

    if(options != null)
    {
        if (options.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
        {
            //was started by tapping a local notification when app wasn't previously running.
            //works if using UNUserNotificationCenter.Current.AddNotificationRequest OR UIApplication.SharedApplication.PresentLocalNotificationNow);

            var localNotification = options[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;

            //I would recommended a key such as this :
            var requestId = localNotification.UserInfo["RequestId"].ToString();
        }               
    }
    return true;
}
Wes
fuente
2

Directamente de la documentación para

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo:nil

Si la aplicación se está ejecutando y recibe una notificación remota, la aplicación llama a este método para procesar la notificación.

Su implementación de este método debe usar la notificación para tomar un curso de acción apropiado.

Y un poco mas tarde

Si la aplicación no se está ejecutando cuando llega una notificación push, el método inicia la aplicación y proporciona la información adecuada en el diccionario de opciones de inicio.

La aplicación no llama a este método para manejar esa notificación push.

En cambio, su implementación de la

application:willFinishLaunchingWithOptions:

o

application:didFinishLaunchingWithOptions:

El método necesita obtener los datos de la carga útil de notificaciones push y responder adecuadamente.

Pfitz
fuente
2

Voy a empezar con una tabla de estado que he creado para mi propio uso para visualizar con mayor precisión y considerar todos los otros estados: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkU3iLdZnOZoevkMzOeng7gs31IFhD-L/pubhtml ? gid = 0 & single = true

Usando este cuadro, podemos ver lo que realmente se requiere para desarrollar un sistema robusto de manejo de notificaciones que funcione en casi todos los casos de uso posibles.

Solución completa ↓

  • Almacenar la carga útil de notificaciones en didReceiveRemoteNotification
  • Borrar la notificación almacenada en applicationWillEnterForeground y didFinishLaunchingWithOptions
  • Para abordar los casos en los que se extrajo el Centro de control / Centro de notificaciones, puede usar un indicador willResignActiveCalled y establecerlo en falso inicialmente. Establezca esto en verdadero en el método applicationWillResignActive ,
  • En el método didReceiveRemoteNotification , guarde las notificaciones (userInfo) solo cuando willResignActiveCalled sea falso.
  • Restablecer willResignActiveCalled a false en applicationDidEnterBackground y applicationDidBecomeActive método.

Nota: Se sugiere una respuesta similar en los comentarios sobre la respuesta de Eric, sin embargo, la hoja de estado ayuda a encontrar todos los escenarios posibles como lo hice en mi aplicación.

Encuentre el código completo a continuación y comente a continuación si no se maneja un caso específico:

AppDelegate

class AppDelegate: UIResponder, UIApplicationDelegate {
  private var willResignActiveCalled = false

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    NotificationUtils.shared.notification = nil
    return true
  }
  func applicationWillResignActive(_ application: UIApplication) {
    willResignActiveCalled = true
  }
  func applicationDidEnterBackground(_ application: UIApplication) {
    willResignActiveCalled = false
  }
  func applicationWillEnterForeground(_ application: UIApplication) {
    NotificationUtils.shared.notification = nil
  }
  func applicationDidBecomeActive(_ application: UIApplication) {
    willResignActiveCalled = false
    NotificationUtils.shared.performActionOnNotification()
  }
  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if !willResignActiveCalled { // Check if app is in inactive by app switcher, control center, or notification center
      NotificationUtils.shared.handleNotification(userInfo: userInfo)
    }
  }
}

NotificationUtils : aquí es donde puede escribir todo su código para navegar a diferentes partes de la aplicación, manejar bases de datos (CoreData / Realm) y hacer todo lo demás que debe hacerse cuando se recibe una notificación.

   class NotificationUtils {
  static let shared = NotificationUtils()
  private init() {}

  var notification : [AnyHashable: Any]?

  func handleNotification(userInfo : [AnyHashable: Any]){
    if UIApplication.shared.applicationState == UIApplicationState.active {
      self.notification = userInfo //Save Payload
      //Show inApp Alert/Banner/Action etc
      // perform immediate action on notification
    }
    else if UIApplication.shared.applicationState == UIApplicationState.inactive{
      self.notification = userInfo
    }
    else if UIApplication.shared.applicationState == UIApplicationState.background{
      //Process notification in background,
      // Update badges, save some data received from notification payload in Databases (CoreData/Realm)
    }
  }

  func performActionOnNotification(){
    // Do all the stuffs like navigating to ViewControllers, updating Badges etc
    defer {
      notification = nil
    }
  }
}
chetan anand
fuente
Es mejor poner esto como un comentario ya que esta no es la respuesta.
Maddy
@Maddy Gracias por su sugerencia, Actualicé la respuesta con todos los detalles
chetan y
1
func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
    print("Push notification received: \(data)")

    if let info = data["aps"] as? Dictionary<String, AnyObject> {
        let alertMsg = info["alert"] as! String
        print(alertMsg)
        switch application.applicationState {
        case .active:
            print("do stuff in case App is active")
        case .background:
            print("do stuff in case App is in background")
           // navigateToChatDetailViewControler(pushdata: data)
        case .inactive:
            print("do stuff in case App is inactive")
            // navigateToChatDetailViewControler(pushdata: data)
        }
    }
}
Hardik Bar
fuente
1

Solo hay una forma confiable y funciona solo para iOS 10+ :

Usando el método de UNUserNotificationCenterimplementación UNUserNotificationCenterDelegate:

- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {

    //Here you can get your original push if you need to
    NSDictionary* pusDict = response.notification.request.content.userInfo;

    if ([response.actionIdentifier isEqualToString: UNNotificationDefaultActionIdentifier]) {
        //User tapped the notification
    } else if ([response.actionIdentifier isEqualToString: UNNotificationDismissActionIdentifier]) {
        //User dismissed the notification 
    } else if ([response.actionIdentifier isEqualToString: MYCustomActionId]) {
        //User chose my custom defined action
    }
    ...
}
Luten
fuente
0

Puedes usar:

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo

para manejar las notificaciones push remotas.

Consulta aquí la documentación

sunkehappy
fuente
0
     // shanegao's code in Swift 2.0
     func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
    {
            if ( application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background ){
                    print("opened from a push notification when the app was on background")
            }else{
                    print("opened from a push notification when the app was on foreground")
            }
    }
Sean Dev
fuente
Pero qué pasa si la aplicación se cerró (finalizó). Al igual que Twitter o Instagram, de alguna manera lo detecta y, si la aplicación está cerrada, lo redirige a nuevas publicaciones o imágenes o su perfil, etc.
Tarvo Mäesepp
0

El problema con esta pregunta es que "abrir" la aplicación no está bien definida. Una aplicación se inicia en frío desde un estado que no se está ejecutando o se reactiva desde un estado inactivo (por ejemplo, al volver a cambiarla desde otra aplicación). Aquí está mi solución para distinguir todos estos posibles estados:

typedef NS_ENUM(NSInteger, MXAppState) {
    MXAppStateActive = 0,
    MXAppStateReactivated = 1,
    MXAppStateLaunched = 2
};

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // ... your custom launch stuff
    [[MXDefaults instance] setDateOfLastLaunch:[NSDate date]];
    // ... more custom launch stuff
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // Through a lot of trial and error (by showing alerts), I can confirm that on iOS 10
    // this method is only called when the app has been launched from a push notification
    // or when the app is already in the Active state.  When you receive a push
    // and then launch the app from the icon or apps view, this method is _not_ called.
    // So with 99% confidence, it means this method is called in one of the 3 mutually exclusive cases
    //    1) we are active in the foreground, no action was taken by the user
    //    2) we were 'launched' from an inactive state (so we may already be in the main section) by a tap
    //       on a push notification
    //    3) we were truly launched from a not running state by a tap on a push notification
    // Beware that cases (2) and (3) may both show UIApplicationStateInactive and cant be easily distinguished.
    // We check the last launch date to distinguish (2) and (3).

    MXAppState appState = [self mxAppStateFromApplicationState:[application applicationState]];
    //... your app's logic
}

- (MXAppState)mxAppStateFromApplicationState:(UIApplicationState)state {
    if (state == UIApplicationStateActive) {
        return MXAppStateActive;
    } else {
        NSDate* lastLaunchDate = [[MXDefaults instance] dateOfLastLaunch];
        if (lastLaunchDate && [[NSDate date] timeIntervalSinceDate:lastLaunchDate] < 0.5f) {
            return MXAppStateLaunched;
        } else {
            return MXAppStateReactivated;
        }
    }
    return MXAppStateActive;
}

Y MXDefaultses solo un pequeño envoltorio para NSUserDefaults.

skensell
fuente
0

por swift

 func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]){

    ++notificationNumber
    application.applicationIconBadgeNumber =  notificationNumber;

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

        var message = aps["alert"]
        println("my messages : \(message)")

    }
}
idris yıldız
fuente
0

Xcode 10 Swift 4.2

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {

    let state : UIApplicationState = application.applicationState
    if (state == .Inactive || state == .Background) {
        // coming from background
    } else {
        // App is running in foreground
    }
}
Prashant Gaikwad
fuente
0

Para iOS 10+, puede usar este método para saber cuándo se hace clic en su notificación, independientemente del estado de la aplicación.

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    //Notification clicked
    completionHandler()
}
manishsharma93
fuente
0

La respuesta de M.Othman es correcta para aplicaciones que no contienen delegado de escena Para aplicaciones de delegado de escena Esto funcionó para mí en iOS 13

Aquí está el código para el que se debe escribir se conectará escena

if connectionOptions.notificationResponse == nil { 
//Not opened from push notification
} else {
  //Opened from push notification
}

El código para el delegado de la aplicación para admitir versiones anteriores didFinishLaunchingWithOptions

let notification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification]
        if (notification != nil) {

            //Launched from push notification
        } else {

            //Launch from other source
        }
Noman Haroon
fuente
-1

Para usuarios rápidos:

Si desea iniciar una página diferente al abrir desde push o algo así, debe registrarla didFinishLaunchingWithOptionscomo:

let directVc: directVC! = directVC(nibName:"directVC", bundle: nil)
let pushVc: pushVC! = pushVC(nibName:"pushVC", bundle: nil)

if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
     self.navigationController = UINavigationController(rootViewController: pushVc!)
} else {
     self.navigationController = UINavigationController(rootViewController: directVc!)
}
self.window!.rootViewController = self.navigationController
AAA
fuente
El delegado no tiene miembro de navegación
Controlador
1
Cree un controlador de navegación en el archivo AppDelegate.h. Lo estoy usando y funciona!
AAA
-1

EN SWIFT:

Estoy ejecutando notificaciones push (con recuperación de fondo). Cuando mi aplicación está en segundo plano y recibo una notificación push, descubrí que didReceiveRemoteNotification en appDelegate se llamaría dos veces; una vez cuando se recibe la notificación y otra cuando el usuario hace clic en la alerta de notificación.

Para detectar si se hizo clic en la alerta de notificación, solo verifique si applicationState raw value == 1 inside didReceiveRemoteNotification en appDelegate.

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject: AnyObject]) {
    // If not from alert click applicationState(1)
    if (application.applicationState.rawValue != 1) {
        // Run your code here
    }
}

Espero que esto ayude.

Johnny5
fuente
-1

Cuando la aplicación está en segundo plano como shanegao , puedes usar

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground  )
    {
         //opened from a push notification when the app was on background
    }
}

Pero si desea iniciar la aplicación y cuando la aplicación está cerrada y desea depurar su aplicación, puede ir a Editar esquema y en el menú de la izquierda seleccionar Ejecutar y luego en Ejecutar seleccionar Esperar a que se ejecute el ejecutable y luego iniciar la aplicación cuando haga clic en notificación push

Editar esquema> Ejecutar> Esperar a que se ejecute el ejecutable

salmancs43
fuente