Détecter si l'application a été lancée / ouverte à partir d'une notification push

172

Est-il possible de savoir si l'application a été lancée / ouverte à partir d'une notification push?

Je suppose que l'événement de lancement peut être capturé ici:

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

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

    }
}

Cependant, comment puis-je détecter qu'elle a été ouverte à partir d'une notification push lorsque l'application était en arrière-plan?

Joao
la source
6
C'est un article ancien mais très utile. Malheureusement, les principales réponses ne résolvent pas réellement le problème (comme l'indiquent les commentaires). Veuillez envisager de marquer une nouvelle réponse comme «acceptée» car la réponse actuelle n'est pas complète.
MobileVet
1
Cette question a plus de 100 000 vues, mais la réponse sélectionnée est incorrecte ou complète. Pour les visiteurs, pensez à trier par Actif plutôt que par Votes pour trouver des solutions modernes.
Albert Renshaw le

Réponses:

187

Voir ce code:

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

pareil que

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
shanegao
la source
19
@ManuelM. C'est une bonne réponse en ce sens qu'elle montre comment détecter lorsqu'une application en arrière-plan est mise au premier plan à partir d'une notification push. Pour lorsque l'application n'est pas en cours d'exécution, vous avez besoin de la réponse de M.Othman ci-dessous.
OpenUserX03
6
Je reçois l'appel à l'application: didReceiveRemoteNotification: après avoir appuyé sur la notification, que l'application soit juste en arrière-plan ou qu'elle ne fonctionne pas du tout, cette réponse répond parfaitement à mes besoins. Testé sur iOS 7 & 8
Newtz
16
Comme certains l'ont souligné, cela ne détecte pas "lancé / ouvert à partir d'une notification push". Ceci est appelé lorsque la notification est reçue, pas lorsqu'elle est ouverte. Donc, si vous avez reçu une notification dans le bg mais que vous avez appuyé sur l'icône de l'application pour ouvrir l'application, le code que vous avez ici continuera à s'exécuter et vous pourriez ouvrir une page que l'utilisateur n'avait pas l'intention d'ouvrir.
Bao Lei
4
@ManuelM. cette méthode ne dit pas si l'application a été ouverte via le centre de notification ou l'icône de l'application si les modes d'arrière-plan - notification à distance sont cochés. C'est le cas quand il n'est pas coché. J'ai documenté la différence dans cet article: stackoverflow.com/questions/32061897/…
Bao Lei
2
Confirmé que cela fonctionne avec Google Cloud Messaging.
CularBytes
127

en retard mais peut-être utile

Lorsque l'application n'est pas en cours d'exécution

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

est appelé ..

où vous devez vérifier la notification 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
la source
2
Notez que dans l'extrait de code ci-dessus, la notification ne doit pas être déclarée comme un (UILocalNotification *) mais comme un (NSDictionary *)
cosmix
1
De cette façon, vous pouvez voir s'il y avait des notifications pour l'application, alors qu'elle n'était pas en cours d'exécution! La question était de savoir comment détecter si l'application était ouverte à partir d'une notification. Dans ce cas, le didReceiveRemoteNotification est appelé, même si l'application ne fonctionnait pas du tout. - J'aime votre réponse, car elle est assez importante dans de nombreux cas, mais ce n'est pas la bonne réponse à la question.
Axel Zehden
Votre réponse et cette réponse font-elles la même chose?
Honey
38

Le problème que nous avons rencontré était de mettre à jour correctement la vue après le lancement de l'application. Il y a ici des séquences compliquées de méthodes de cycle de vie qui prêtent à confusion.

Méthodes du cycle de vie

Nos tests pour iOS 10 ont révélé les séquences suivantes de méthodes de cycle de vie pour les différents cas:

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

Le problème

Ok, alors maintenant nous devons:

  1. Déterminer si l'utilisateur ouvre l'application à partir d'un push
  2. Mettre à jour la vue en fonction de l'état du push
  3. Effacez l'état afin que les ouvertures suivantes ne ramènent pas l'utilisateur à la même position.

Le problème est que la mise à jour de la vue doit avoir lieu lorsque l'application devient réellement active, ce qui est la même méthode de cycle de vie dans tous les cas.

Esquisse de notre solution

Voici les principaux composants de notre solution:

  1. Stockez une notificationUserInfovariable d'instance sur AppDelegate.
  2. Définissez notificationUserInfo = nilà la fois applicationWillEnterForegroundet didFinishLaunchingWithOptions.
  3. mettre notificationUserInfo = userInfoendidReceiveRemoteNotification:inactive
  4. De applicationDidBecomeActivetoujours appeler une méthode personnalisée openViewFromNotificationet passer self.notificationUserInfo. Si la valeur self.notificationUserInfoest nulle, retournez tôt, sinon ouvrez la vue en fonction de l'état de notification trouvé dans self.notificationUserInfo.

Explication

Lors de l'ouverture à partir d'un push didFinishLaunchingWithOptionsou applicationWillEnterForegroundest toujours appelé immédiatement avant didReceiveRemoteNotification:inactive, nous avons donc d'abord réinitialisé notificationUserInfo dans ces méthodes afin qu'il n'y ait pas d'état périmé. Ensuite, si didReceiveRemoteNotification:inactiveest appelé, nous savons que nous ouvrons à partir d'un push, donc nous définissons self.notificationUserInfoce qui est ensuite capté par applicationDidBecomeActivepour transférer l'utilisateur vers la bonne vue.

Il y a un dernier cas qui est si l'utilisateur a l'application ouverte dans le commutateur d'application (c'est-à-dire en appuyant deux fois sur le bouton d'accueil pendant que l'application est au premier plan), puis reçoit une notification push. Dans ce cas, seul didReceiveRemoteNotification:inactiveest appelé, et ni WillEnterForeground ni didFinishLaunching ne sont appelés, vous avez donc besoin d'un état spécial pour gérer ce cas.

J'espère que cela t'aides.

Eric Conner
la source
Enfin quelque chose qui fonctionne, merci! Je voulais créer un drapeau "appResuming" et ouvrir l'écran dans les receiveméthodes lorsque l'état de l'application est actif ou que l'application reprend. Cela pourrait entraîner des problèmes de modification des VC lorsque l'application est toujours inactive. Votre solution est superbe, jusqu'à ce qu'Apple modifie à nouveau le cycle de vie.
shelll
Qu'en est-il d'iOS 9, les méthodes de cycle de vie sont-elles appelées de la même manière et dans l'ordre? Je n'ai déjà aucun appareil iOS 9, je ne peux donc pas le tester correctement.
shelll
2
Il existe deux autres cas de bord, à l'exception du sélecteur d'application. 1) Lorsque le centre de notification est tiré du haut et recouvre l'application 2) Lorsque le panneau iOS avec wifi / BT / etc est tiré du bas et superpose l'application. Dans les trois cas, seul le applicationWillResignActiveest appelé, puis le applicationDidBecomeActive. Ainsi, après l' applicationWillResignActiveappel, ne sauvegardez pas la notification reçue jusqu'à ce que applicationDidEnterBackgroundou applicationDidBecomeActivesoit appelé.
shelll
Merci d'avoir ajouté ces cas @shelll. Cela devient toujours plus compliqué! Je ne suis pas sûr d'iOS9. Je dirais qu'il est probablement prudent de supposer qu'ils sont identiques, mais qui sait.
Eric Conner
Juste un avertissement. Je testais iOS 11 Beta 9 aujourd'hui et j'ai constaté que dans le cas où vous avez votre application au premier plan, verrouillez le téléphone, puis sélectionnez une notification push sur l'écran de verrouillage, il appelle didReceiveRemoteNotification: background juste avant d'appeler applicationWillEnterForeground plutôt que ce que nous voyons sur iOS 10 où il appelle applicationWillEnterForeground puis didReceiveRemoteNotification: inactif - il s'agit donc d'un cas de pointe non encore couvert. À mon avis, il s'agit d'un bogue dans le code iOS, mais étant donné la proximité de la version iOS 11, il faut être conscient.
Roy
24

C'est un article bien usé ... mais il manque encore une solution réelle au problème (comme cela est souligné dans les différents commentaires).

La question initiale concerne la détection du moment où l'application a été lancée / ouverte à partir d'une notification push, par exemple un utilisateur appuie sur la notification. Aucune des réponses ne couvre réellement ce cas.

La raison peut être vue dans le flux d'appels lorsqu'une notification arrive, application:didReceiveRemoteNotification...

est appelé lorsque la notification est reçue ET à nouveau lorsque la notification est tapée par l'utilisateur. Pour cette raison, vous ne pouvez pas dire en regardant simplement UIApplicationStatesi l'utilisateur l'a tapé.

De plus, vous n'avez plus besoin de gérer la situation d'un `` démarrage à froid '' de l'application application:didFinishLaunchingWithOptions...comme application:didReceiveRemoteNotification...on l'appelle à nouveau après le lancement dans iOS 9+ (peut-être aussi 8).

Alors, comment pouvez-vous savoir si l'utilisateur tap a commencé la chaîne d'événements? Ma solution consiste à marquer l'heure à laquelle l'application commence à sortir de l'arrière-plan ou à démarrer à froid, puis à enregistrer cette heure application:didReceiveRemoteNotification.... S'il est inférieur à 0,1 s, vous pouvez être à peu près sûr que le robinet a déclenché le démarrage.

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

J'ai testé cela pour les deux cas (application en arrière-plan, application non exécutée) sur iOS 9+ et cela fonctionne comme un charme. 0,1 s est également assez prudent, la valeur réelle est de ~ 0,002 s, donc 0,01 convient également.

MobileVet
la source
1
Cela semble être la seule solution de travail qui fait la différence entre appuyer réellement sur la notification et ouvrir la barre d'état sur l'application.
liviucmg
4
C'est la seule solution fonctionnelle de tout le StackOverflow. La seule chose que je voudrais ajouter, c'est que lorsque vous prenez en charge iOS 10 et supérieur, vous pouvez simplement utiliser l' UNNotificationCenterAPI, en particulier les méthodes UNNotificationCenterDelegate. Ces API appellent la userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) méthode func uniquement lorsque l'utilisateur a réellement tapé sur la notification.
DenHeadless
à quoi ressemble-t-il pour Swift 3?
Jochen Österreicher
La solution ne fonctionne pas lorsqu'une application est à l'état inactif (l'utilisateur fait glisser le centre de notification vers le bas ou le centre de contrôle vers le haut) et reçoit une notification. Lorsque l'utilisateur appuie sur la notification, l'application ne reçoit pas d' applicationWillEnterForeground appel, par conséquent, la solution ne parvient pas à détecter le robinet.
DevGansta
@DevGansta Lorsque vous ajoutez votre classe comme UNUserNotificationCenter.current().delegatedans application:didFinishLaunchingWithOptions, l'application appellera userNotificationCenter(didReceive response)après le robinet dans le cas que vous avez décrit
Dorian Roy
22

Lorsque l'application est terminée et que l'utilisateur appuie sur la notification push

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

Lorsque l'application est en arrière-plan et que l'utilisateur appuie sur la notification push

Si l'utilisateur ouvre votre application à partir de l'alerte affichée par le système, le système peut appeler à nouveau cette méthode lorsque votre application est sur le point d'entrer au premier plan afin que vous puissiez mettre à jour votre interface utilisateur et afficher les informations relatives à la notification.

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

En fonction de votre application, il peut également vous envoyer un push silencieux à l' content-availableintérieur aps, alors soyez conscient de cela également :) Voir https://stackoverflow.com/a/33778990/1418457

onmyway133
la source
2
Seule réponse qui ne ressemble pas à un hack sale et correct. Ce qui me manque, c'est si l'application est en arrière-plan et que l'utilisateur l'ouvre manuellement, comment vérifier cela? Tout en étant toujours en mesure de vérifier le démarrage à froid par poussée et de pousser depuis l'arrière-plan.
Jochen Österreicher
1
@ JochenÖsterreicher Salut, je le résume ici, s'il vous plaît vérifier medium.com/@onmyway133/…
onmyway133
19

Swift 2.0 pour l'état `` non exécuté '' (notification locale et distante)

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
la source
15

En application:didReceiveRemoteNotification:vérifier si vous avez reçu la notification lorsque votre application est au premier plan ou arrière - plan.

S'il a été reçu en arrière-plan, lancez l'application à partir de la notification.

-(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
la source
3
Notez que "Application ouverte à partir de la notification" sera un faux positif si la notification est envoyée alors que l'utilisateur est sur un écran différent (par exemple, s'il abaisse la barre d'état et reçoit ensuite une notification de votre application).
Kevin Cooper
4
@Kevin Exactement. Cela vous fait vous demander pourquoi Apple a apparemment mis un stagiaire pour concevoir le processus de gestion des notifications ...
Andreas
comment pouvons-nous détecter si nous tapons sur la notification reçue à l'état actif
Mayank Jain
13

Pour rapide:

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
la source
4

Oui, vous pouvez détecter par cette méthode dans appDelegate :

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

Pour la notification locale:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}
Impossible
la source
1
Cette méthode n'est pas appelée si l'application n'est pas en cours d'exécution. C'est ce qui a demandé ici
Pfitz
Mon problème n'est pas de gérer la notification, mais plutôt de savoir si elle a été ouverte lorsque vous cliquez sur la bannière (lorsque l'application est en arrière-plan).
joao
3

si quelqu'un veut la réponse dans 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
la source
mais comment savoir si l'application est ouverte en appuyant sur la notification push lorsque l'application est terminée
user3804063
1
lorsque quelqu'un appuie sur le push, l'application sera ouverte, qu'elle soit terminée ou non. et l'affaire .inactive appelle
Hamid Shahsavari
1
J'ai besoin de détecter si l'application est ouverte en appuyant sur le bouton poussoir et souhaite accéder au contenu respectif que j'ai vu instagram faire cela
user3804063
Et les notifications locales?
Amir Shabani le
3

Publier ceci pour les utilisateurs Xamarin.

La clé pour détecter si l'application a été lancée via une notification push est la AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)méthode et le dictionnaire d'options transmis.

Le dictionnaire des options aura cette clé si elle est une notification locale: UIApplication.LaunchOptionsLocalNotificationKey.

S'il s'agit d'une notification à distance, ce sera le cas UIApplication.LaunchOptionsRemoteNotificationKey.

Lorsque la clé est LaunchOptionsLocalNotificationKey, l'objet est de type UILocalNotification. Vous pouvez ensuite consulter la notification et déterminer de quelle notification il s'agit.

Astuce de pro: UILocalNotificationn'a pas d'identifiant dedans, de la même manière UNNotificationRequest. Placez une clé de dictionnaire dans UserInfo contenant un requestId afin que, lors du test de UILocalNotification, vous ayez un requestId spécifique disponible sur lequel baser une logique.

J'ai constaté que même sur les appareils iOS 10+, lors de la création de notifications de localisation à l'aide du UNUserNotificationCenter's AddNotificationRequest& UNMutableNotificationContent, lorsque l'application n'est pas en cours d'exécution (je l'ai tuée), et qu'elle est lancée en appuyant sur la notification dans le centre de notification, que le dictionnaire contient toujours l' UILocalNotificaitonobjet.

Cela signifie que mon code qui vérifie le lancement basé sur les notifications fonctionnera sur les appareils iOS8 et 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;
}
Nous s
la source
2

Directement à partir de la documentation pour

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

Si l'application est en cours d'exécution et reçoit une notification à distance, l'application appelle cette méthode pour traiter la notification.

Votre implémentation de cette méthode doit utiliser la notification pour prendre une mesure appropriée.

Et un peu plus tard

Si l'application n'est pas en cours d'exécution lorsqu'une notification push arrive, la méthode lance l'application et fournit les informations appropriées dans le dictionnaire des options de lancement.

L'application n'appelle pas cette méthode pour gérer cette notification push.

Au lieu de cela, votre implémentation du

application:willFinishLaunchingWithOptions:

ou

application:didFinishLaunchingWithOptions:

La méthode doit obtenir les données de charge utile de notification push et répondre de manière appropriée.

Pfitz
la source
2

Je vais commencer par un graphique d'état que j'ai créé pour ma propre utilisation afin de le visualiser plus précisément et de prendre en compte tous les autres états: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASl0bEATqeL3P9UKpBwASlT6ZkU3htgbwASlT6ZkU3htgdlzlzlzlg ? gid = 0 & single = true

En utilisant ce graphique, nous pouvons voir ce qui est réellement nécessaire pour développer un système de gestion des notifications robuste qui fonctionne dans presque tous les cas d'utilisation possibles.

Solution complète ↓

  • Stocker la charge utile de notification dans didReceiveRemoteNotification
  • Effacer la notification stockée dans applicationWillEnterForeground et didFinishLaunchingWithOptions
  • Pour résoudre les cas où le centre de contrôle / centre de notification a été retiré, vous pouvez utiliser un indicateur willResignActiveCalled et le définir initialement sur false , définissez -le sur true dans la méthode applicationWillResignActive ,
  • Dans la méthode didReceiveRemoteNotification , enregistrez les notifications (userInfo) uniquement lorsque willResignActiveCalled a la valeur false.
  • Réinitialisez willResignActiveCalled sur false dans la méthode applicationDidEnterBackground et applicationDidBecomeActive .

Remarque: une réponse similaire est suggérée dans les commentaires sur la réponse d'Eric, cependant, la feuille d'état aide à trouver tous les scénarios possibles, comme je l'ai fait dans mon application.

Veuillez trouver le code complet ci-dessous et commenter ci-dessous si un cas spécifique n'est pas traité:

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 : C'est ici que vous pouvez écrire tout votre code pour naviguer vers différentes parties de l'application, gérer les bases de données (CoreData / Realm) et faire toutes les autres tâches à effectuer lorsqu'une notification est reçue.

   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
la source
mieux vaut mettre cela en commentaire car ce n'est pas la réponse.
Maddy
@Maddy Merci pour la suggestion, mise à jour de la réponse avec tous les détails
chetan anand
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)
        }
    }
}
Bar Hardik
la source
1

Il n'y a qu'un seul moyen fiable, et cela ne fonctionne que pour iOS 10+ :

Utilisation de la méthode de UNUserNotificationCentermise en œuvre 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
la source
0

Vous pouvez utiliser:

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

pour gérer les notifications push à distance.

Consultez ici la documentation

soleil heureux
la source
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
la source
Mais que faire si l'application était fermée (arrêtée). Comme Twitter ou Instagram, il le détecte d'une manière ou d'une autre et si l'application est même fermée, elle vous redirige vers un nouveau message ou des photos ou votre profil, etc.
Tarvo Mäesepp
0

Le problème avec cette question est que «l'ouverture» de l'application n'est pas bien définie. Une application est soit lancée à froid à partir d'un état non en cours d'exécution, soit réactivée à partir d'un état inactif (par exemple, pour y revenir à partir d'une autre application). Voici ma solution pour distinguer tous ces états possibles:

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

Et MXDefaultsc'est juste un petit emballage pour NSUserDefaults.

Skensell
la source
0

Pour 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
la source
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
la source
0

Pour iOS 10+, vous pouvez utiliser cette méthode pour savoir quand votre notification est cliquée, quel que soit l'état de l'application.

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

    //Notification clicked
    completionHandler()
}
manishsharma93
la source
0

La réponse de M.Othman est correcte pour les applications qui ne contiennent pas de délégué de scène Pour les applications de délégué de scène Cela a fonctionné pour moi sur iOS 13

Voici le code pour qui doit être écrit dans la scène de connexion

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

Code pour que le délégué d'application prenne en charge les versions antérieures didFinishLaunchingWithOptions

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

            //Launched from push notification
        } else {

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

Pour les utilisateurs Swift:

Si vous souhaitez lancer une page différente à l'ouverture de push ou quelque chose comme ça, vous devez l'enregistrer didFinishLaunchingWithOptionscomme:

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
la source
Delegate n'a pas de membre navigationController
Pablo Cegarra
1
Créez un contrôleur de navigation dans le fichier AppDelegate.h. Je l'utilise et ça marche!
AAA
-1

DANS SWIFT:

J'exécute des notifications push (avec récupération en arrière-plan). Lorsque mon application est en arrière-plan et que je reçois une notification push, j'ai trouvé que didReceiveRemoteNotification dans appDelegate serait appelé deux fois; une fois lorsque la notification est reçue et une autre lorsque l'utilisateur clique sur l'alerte de notification.

Pour détecter si une alerte de notification a été cliquée, vérifiez simplement si la valeur brute applicationState == 1 dans didReceiveRemoteNotification dans 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
    }
}

J'espère que ça aide.

Johnny5
la source
-1

Lorsque l'application est en arrière-plan en tant que shanegao, vous pouvez utiliser

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

Mais si vous souhaitez lancer l'application et lorsque l'application est fermée et que vous souhaitez déboguer votre application, vous pouvez aller dans Modifier le schéma et dans le menu de gauche, sélectionnez Exécuter , puis au lancement, sélectionnez Attendre le lancement de l'exécutable , puis le lancement de l'application lorsque vous cliquez sur la notification push

Modifier le schéma> Exécuter> Attendre le lancement de l'exécutable

salmancs43
la source