Notification push iOS: comment détecter si l'utilisateur a tapé sur la notification lorsque l'application est en arrière-plan?

116

Il y a beaucoup de threads stackoverflow concernant ce sujet, mais je n'ai toujours pas trouvé de bonne solution.

Si l'application n'est pas en arrière - plan, je peux vérifier launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]en application:didFinishLaunchingWithOptions:appel pour voir si elle est ouverte à partir d' une notification.

Si l'application est en arrière-plan, tous les articles suggèrent d'utiliser application:didReceiveRemoteNotification:et de vérifier l'état de l'application. Mais comme j'ai expérimenté (et aussi comme le nom de cette API le suggère), cette méthode est appelée lorsque la notification est reçue, au lieu d'être tapée.

Le problème est donc que si l'application est lancée puis mise en arrière-plan et que vous savez qu'une notification est reçue de application:didReceiveNotification( application:didFinishLaunchWithOptions:ne se déclenchera pas à ce stade), comment savoir si l'utilisateur a repris l'application à partir de en appuyant sur la notification ou en appuyant simplement sur le icône de l'application? Parce que si l'utilisateur a tapé sur la notification, l'attente est d'ouvrir la page mentionnée dans cette notification. Sinon, ça ne devrait pas.

Je pourrais utiliser handleActionWithIdentifierpour les notifications d'action personnalisées, mais cela ne se déclenche que lorsqu'un bouton d'action personnalisé est appuyé, pas lorsque l'utilisateur appuie sur le corps principal de la notification.

Merci.

ÉDITER:

après avoir lu une réponse ci-dessous, je me suis dit que je pouvais clarifier ma question:

Comment différencier ces 2 scénarios:

(A) 1.app passe à l'arrière-plan; 2. notification reçue; 3. l'utilisateur appuie sur la notification; 4. l'application entre au premier plan

(B) 1.app passe à l'arrière-plan; 2. notification reçue; 3. l'utilisateur ignore la notification et appuie sur l'icône de l'application plus tard; 4. l'application entre au premier plan

Depuis application:didReceiveRemoteNotification:est déclenché dans les deux cas à l'étape 2.

Ou, devrait-il application:didReceiveRemoteNotification:être déclenché à l'étape 3 pour (A) uniquement, mais j'ai en quelque sorte mal configuré mon application, donc je la vois à l'étape 2?

Bao Lei
la source
Utilisez une valeur de dictionnaire personnalisée pour votre charge utile et agissez en conséquence. Assez simple.
soulshined
@soulshined un dictionnaire dans la charge utile peut indiquer si l'utilisateur a tapé sur la notification, non? Par exemple, votre ami A a publié un article B, vous pouvez dire {utilisateur: A, article: B} dans la charge utile, tandis que l'application est en arrière-plan et que vous obtenez didReceiveRemoteNotification. Comment savoir quand l'application reprend, si vous devez afficher l'article?
Bao Lei
2
@soulshined J'ai lu la documentation et je me suis renseigné sur ce que fait didReceiveRemoteNotification. Avez-vous réellement lu ma question? Selon la documentation officielle d'Apple, didReceiveRemoteNotification "indique au délégué que l'application en cours d'exécution a reçu une notification à distance". Je demande quel est un bon moyen de savoir si l'utilisateur a tapé sur une notification. Le lien SO auquel vous avez fait référence concerne le lancement de l'application à partir d'un nouveau départ.Je demande le scénario lorsque l'application est en arrière-plan.
Bao Lei
1
@soulshined OK peut-être que je ne l'ai pas dit assez clairement. Je veux dire si l'application est complètement fermée, pas en arrière-plan, oui didFinishLaunching sera appelé. Mais si vous lancez votre application, puis en arrière-plan l'application, et maintenant une notification entre, et l'utilisateur appuie sur la notification, et maintenant le didFinishLaunching ne sera plus appelé. Au lieu de cela, applicationWillEnterForeground et applicationDidBecomeActive seront appelés. Comment savoir si l'application entre au premier plan parce que l'utilisateur a tapé sur la notification ou l'icône de l'application?
Bao Lei

Réponses:

102

OK, j'ai finalement compris.

Dans les paramètres cibles ➝ onglet Capacités ➝ Modes d'arrière-plan, si vous cochez "Notifications à distance", application:didReceiveRemoteNotification:se déclenchera dès que la notification arrivera (tant que l'application est en arrière-plan), et dans ce cas, il n'y a aucun moyen de dire si l'utilisateur tapera sur la notification.

Si vous décochez cette case, application:didReceiveRemoteNotification:ne sera déclenché que lorsque vous appuyez sur la notification.

C'est un peu étrange que cocher cette case modifie le comportement de l'une des méthodes de délégué d'application. Ce serait plus agréable si cette case est cochée, Apple utilise deux méthodes de délégué différentes pour la réception des notifications et le robinet de notification. Je pense que la plupart des développeurs veulent toujours savoir si une notification est activée ou non.

Espérons que cela sera utile pour quiconque rencontre ce problème. Apple ne l'a pas non plus clairement documenté ici , il m'a donc fallu un certain temps pour comprendre.

entrez la description de l'image ici

Bao Lei
la source
6
J'ai les modes d'arrière-plan complètement réglés sur "OFF", mais je suis toujours averti lorsqu'une notification arrive avec l'application en mode d'arrière-plan.
Gottfried
5
Génial! Cela m'a aidé. C'est dommage cependant que je doive désactiver les notifications à distance. Je souhaite pré-extraire les données lorsqu'une notification push arrive ET détecter l'utilisateur tap. Je ne trouve pas de moyen d'y parvenir.
Peter Fennema
1
J'ai mis le mode d'arrière-plan sur le mode «ON» et j'active les notifications à distance. Impossible de détecter l'événement de notification.
kalim sayyad
1
J'ai le même problème Le mode d'arrière-plan est réglé sur «ON» et activé la notification à distance, mais je ne suis toujours pas averti lorsque la notification arrive à l'application en mode d'arrière-plan
Swapnil Dhotre
@Bao - Je pense que cela entraînera un rejet sur l'Appstore, car cette option est essentiellement utilisée pour télécharger du contenu lié à la notification en arrière-plan. Donc, si vous ne téléchargez aucun contenu, Apple peut rejeter votre application. developer.apple.com/library/ios/documentation/iPhone/Conceptual/…
niks
69

J'ai cherché la même chose que vous et j'ai trouvé une solution qui ne nécessite pas de cocher la notification à distance.

Pour vérifier si l'utilisateur a tapé, ou si l'application est en arrière-plan ou est active, il vous suffit de vérifier l'état de l'application dans

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{

    if(application.applicationState == UIApplicationStateActive) {

        //app is currently active, can update badges count here

    }else if(application.applicationState == UIApplicationStateBackground){

        //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

    }else if(application.applicationState == UIApplicationStateInactive){

        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here

    }

Pour plus d'informations, consultez:

Référence UIKit Framework> Référence de classe UIApplication> UIApplicationState

Dennism
la source
10
Qu'en est-il de l'application qui est UIApplicationStateInactive en raison de l'interaction de l'utilisateur avec le centre de notifications (glisser de haut) ou le centre de contrôle (glisser de bas) lorsque l'application est à l'écran. Apparemment, il n'y a aucun moyen de dire si APNS a été reçu à l'état inactif ou si l'utilisateur l'a réellement tapé.
Kostia Kim le
1
@KostiaKim Vous avez raison, mais c'est un cas super bord ... à part ça, cette réponse est la plus valable en termes de séparation entre l'application au premier plan ... l'utilisateur tapant ... l'application en arrière
Chérie
Merci, ce genre de démystifie tout. N'oubliez pas que pour que la méthode déléguée soit appelée lorsque l'application est en arrière-plan, la notification push doit inclure la content-availableclé, mais la notification doit alors être silencieuse (c'est-à-dire ne pas inclure de son ou de badge) comme indiqué dans la documentation officielle. .
Nickkk
1
@KostiaKim J'ai en fait pu résoudre le problème de ne pas savoir si le centre de contrôle était ouvert ou si l'utilisateur a réellement tapé sur la notification en ajoutant un booléen défini sur true dans le func applicationDidEnterBackground(_ application: UIApplication)et false dans le, func applicationDidBecomeActive(_ application: UIApplication)cela m'a permis d'afficher l'application dans l'application notifications lorsque l'application est inactive en raison du centre de contrôle ou de la liste des notifications
DatForis
3
Je suis stupéfaite de savoir pourquoi Apple n'a pas présenté d'événement différent ou n'a simplement pas ajouté un indicateur dans les métadonnées pour indiquer que l'utilisateur a réellement interagi avec la notification. Devoir «déduire» si l'utilisateur a effectué une action en fonction des informations ambiantes sur l'état de l'application est assez déraisonnable pour un élément clé d'informations qui affecte les attentes de l'utilisateur concernant le comportement de l'application. La seule chose à faire est peut-être de créer une action personnalisée pour ouvrir l'application avec ces informations?
Allan Nienhuis
20

Selon iOS / XCode: comment savoir que l'application a été lancée en cliquant sur la notification ou sur l'icône de l'application Springboard? vous devez vérifier l'état de l'application dans didReceiveLocalNotification comme ceci:

if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive)
{
    // user has tapped notification
}
else
{
    // user opened app from app icon
}

Bien que cela ne me semble pas totalement logique, cela semble fonctionner.

Werner Kratochwil
la source
1
Cela ne fonctionnerait pas dans ce scénario. J'ai essayé ça avant. Lorsque cette case a été cochée (voir ma réponse acceptée pour plus de détails), lorsque la notification arrive, votre ligne avec le commentaire "// l'utilisateur a tapé sur la notification" sera entrée, même si l'utilisateur n'a pas tapé sur la notification (la notification vient d'arriver ).
Bao Lei
3
Je ne suis pas d'accord. J'ai coché "Notifications à distance" dans mes capacités d'arrière-plan et cela fonctionne de la manière décrite dans ma réponse. J'ai également coché "Récupération en arrière-plan". Peut-être que cela cause le changement?
Werner Kratochwil
pourriez-vous s'il vous plaît +1 ma réponse? ;-) J'ai encore besoin de quelques votes pour participer davantage au stackoverflow. Merci beaucoup
Werner Kratochwil
Yupe, c'est la solution. tnx.
Afshin
Notez que ce 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 puis reçoit une notification de votre application).
Kevin Cooper
16

Si quelqu'un le veut dans Swift 3.0

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
    }

pour swift 4

switch UIApplication.shared.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
Sur quel déclencheur nous devons ajouter ce code, didReceiveRemoteNotification ou didFinishLaunchingWithOptions?
Dashrath
2
Sur didReceiveRemoteNotification
Hamid Shahsavari
@Hamid sh ,,,, J'ai reçu une notification push dans tous les états, c'est-à-dire lorsque l'application est au premier plan, en arrière-plan, à proximité (terminée) ..! mais mon problème est de savoir comment incrémenter le nombre de badges lorsque l'application est en arrière-plan et fermer (terminer) l'état ???? Veuillez me dire en détail comment je le fais ....? le nombre de badges d'application augmente uniquement lorsque l'application est à l'état d'avant-garde .....? si possible s'il vous plaît dites-moi en bref .......!
Kiran jadhav
8

Si vous avez coché "Modes d'arrière-plan"> "Notifications à distance" == OUI, appuyez sur l'événement de notification arrivera dans:

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

Ça m'a aidé. Amusez-vous :)

Aleks Osipov
la source
J'ai vu qu'Apple avait ajouté cela, mais je ne savais pas comment obtenir la charge utile personnalisée de la notification de cette façon.
sudo
Cette fonction est appelée lorsque l'application est dans un état d'arrière-plan et devient active et également lorsque l'application démarre à partir de l'état tué
Nikhil Muskur
@NikhilMuskur uniquement si les "notifications à distance" sont activées, hein?
Zorayr
@Zorayr yup c'est vrai
Nikhil Muskur
8

J'ai également rencontré ce problème - mais sur iOS 11 avec le nouveau UserNotifications Framework.

Ici pour moi c'est comme ça:

  • Nouveau lancement: application:didFinishLaunchingWithOptions:
  • Reçu d'un état de fin: application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • Reçu au premier plan: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • Reçu en arrière-plan: userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
pré
la source
C'est la façon de le faire dans iOS 11 +
OhadM
C'est tellement compliqué 🤦‍♂️
Zorayr
4

Dans mon cas, le mode de fond OFF ne faisait aucune différence. Cependant, lorsque l'application a été suspendue et que l'utilisateur a appuyé sur la notification, je pouvais gérer le cas dans cette méthode de rappel:

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

}
DoruChidean
la source
C'est la manière officielle qu'Apple dit. Selon le document d'Apple, il sera appelé chaque fois que l'utilisateur interagit avec l'interface utilisateur de notification push. Si l'application n'est pas en arrière-plan, elle lancera l'application en mode arrière-plan et appellera cette méthode.
Ryan
3

Pour iOS 10 et supérieur, mettez ceci dans AppDelegate, pour savoir que la notification est activée (fonctionne même si l'application est fermée ou ouverte)

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
print("notification tapped here")
}
Yatin Arora
la source
1
C'est la bonne réponse pour iOS 10+. Explication complète fournie sur un autre fil ici: stackoverflow.com/a/41783666/1761357 .
dead_can_dance
2

Il existe deux fonctions pour gérer la notification PushNotification reçue à l'intérieur de la PushNotificationManagerclasse:

class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate{

}

Comme j'ai testé le premier déclencheur dès l'arrivée de la notification

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

    completionHandler(UNNotificationPresentationOptions.alert)

    //OnReceive Notification
    let userInfo = notification.request.content.userInfo
    for key in userInfo.keys {
         Constants.setPrint("\(key): \(userInfo[key])")
    }

    completionHandler([])

}

Et le deuxième lorsque vous appuyez sur la notification:

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

    //OnTap Notification
    let userInfo = response.notification.request.content.userInfo
    for key in userInfo.keys {
        Constants.setPrint("\(key): \(userInfo[key])")
    }

    completionHandler()
}

Je l'ai également testé avec les états ON et OFF de la notification à distance (en arrière-plan)

Saeid
la source
1

SWIFT 5.1

UIApplication.State n'a pas fonctionné pour moi, car une fois que j'ai lu l'empreinte digitale (modale est affichée) dans mon application, une notification est également lancée dans la barre supérieure et l'utilisateur doit cliquer dessus.

J'ai créé

public static var isNotificationFromApp: Bool = false

dans AppDelegateet je l'ai mis truedans mon démarrage viewControllerpuis dans ma notification storyboard/ viewControllerje viens de le vérifier :)

J'espère que cela peut être utile

Hourglasser
la source
-7

Vous pouvez configurer la charge utile de votre notification push pour appeler le délégué d'application application:didReceiveRemoteNotification:fetchCompletionHandler: méthode lorsque l'application est en arrière-plan. Vous pouvez définir un indicateur ici afin que lorsque l'utilisateur lancera votre application la prochaine fois, vous puissiez effectuer votre opération.

À partir de la documentation d'Apple, vous devez utiliser ces méthodes pour télécharger le nouveau contenu associé à la notification push. Pour que cela fonctionne également, vous devez activer la notification à distance à partir des modes d'arrière-plan et votre charge utile de notification push doit contenir une content-availableclé avec sa valeur définie sur 1. Pour plus d'informations, veuillez consulter la section Utilisation des notifications push pour lancer une section de téléchargement d'Apple doc ici .

Une autre façon consiste à avoir le nombre de badges dans la charge utile des notifications push. Ainsi, la prochaine fois que votre application sera lancée, vous pourrez vérifier le nombre de badges d'application. S'il est supérieur à zéro, effectuez également votre opération et supprimez / effacez le nombre de badges du serveur.

J'espère que cela vous aide.

Ameet Dhas
la source
@bhusan avez-vous lu les détails de ma question? Je vois que didReceiveRemoteNotification est appelé lorsque la notification arrive (avant que l'utilisateur n'appuie dessus). Je voulais savoir si l'utilisateur avait tapé dessus.
Bao Lei
Vous n'avez pas répondu à la question d'OP. Il ne veut pas seulement savoir qu'il y a eu une notification. Il veut savoir si l'utilisateur a ouvert l'application en appuyant sur cette notification à distance.
Joe C