iOS-Push-Benachrichtigung: Wie erkennt man, ob der Benutzer auf Benachrichtigung getippt hat, wenn sich die App im Hintergrund befindet?

116

Es gibt viele Stackoverflow-Threads zu diesem Thema, aber ich habe immer noch keine gute Lösung gefunden.

Wenn sich die App nicht im Hintergrund befindet, kann ich einen Anruf einchecken, launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]um application:didFinishLaunchingWithOptions:festzustellen, ob sie über eine Benachrichtigung geöffnet wurde.

Wenn sich die App im Hintergrund befindet, schlagen alle Beiträge vor, application:didReceiveRemoteNotification:den Anwendungsstatus zu verwenden und zu überprüfen. Aber wie ich experimentiert habe (und auch wie der Name dieser API andeutet), wird diese Methode aufgerufen, wenn die Benachrichtigung empfangen wird, anstatt darauf zu tippen.

Das Problem ist also, wenn die App gestartet und dann im Hintergrund ausgeführt wird und Sie wissen, dass eine Benachrichtigung von empfangen wird application:didReceiveNotification( application:didFinishLaunchWithOptions:wird zu diesem Zeitpunkt nicht ausgelöst), woher Sie wissen, ob der Benutzer die App wieder aufgenommen hat, indem Sie auf die Benachrichtigung tippen oder einfach auf tippen App-Symbol? Wenn der Benutzer auf die Benachrichtigung getippt hat, wird erwartet, dass die in dieser Benachrichtigung erwähnte Seite geöffnet wird. Sonst sollte es nicht.

Ich könnte handleActionWithIdentifierfür benutzerdefinierte Aktionsbenachrichtigungen verwenden, dies wird jedoch nur ausgelöst, wenn auf eine benutzerdefinierte Aktionsschaltfläche getippt wird, nicht, wenn der Benutzer auf den Hauptteil der Benachrichtigung tippt.

Vielen Dank.

BEARBEITEN:

Nachdem ich eine Antwort unten gelesen hatte, dachte ich, dass ich auf diese Weise meine Frage klären kann:

Wie können wir diese beiden Szenarien unterscheiden:

(A) 1.app geht in den Hintergrund; 2. Benachrichtigung erhalten; 3. Der Benutzer tippt auf die Benachrichtigung. 4. App tritt in den Vordergrund

(B) 1.app geht in den Hintergrund; 2. Benachrichtigung erhalten; 3. Der Benutzer ignoriert die Benachrichtigung und tippt später auf das App-Symbol. 4. App tritt in den Vordergrund

Da application:didReceiveRemoteNotification:wird in beiden Fällen in Schritt 2 ausgelöst.

Oder sollte application:didReceiveRemoteNotification:in Schritt 3 nur für (A) ausgelöst werden, aber ich habe meine App irgendwie falsch konfiguriert, sodass ich sie in Schritt 2 sehe?

Bao Lei
quelle
Verwenden Sie einen benutzerdefinierten Wörterbuchwert für Ihre Nutzlast und handeln Sie entsprechend. Ziemlich einfach.
Soulshined
@soulshined Ein Wörterbuch in der Nutzlast kann darstellen, ob der Benutzer auf die Benachrichtigung getippt hat, oder? Beispiel: Ihr Freund A hat einen Artikel B gepostet. Sie können {Benutzer: A, Artikel: B} in der Nutzlast sagen, während sich die App im Hintergrund befindet und Sie didReceiveRemoteNotification erhalten. Woher wissen Sie, wann die App fortgesetzt wird und ob Sie den Artikel anzeigen sollen?
Bao Lei
2
@soulshined Ich habe die Dokumentation gelesen und mich darüber informiert, was didReceiveRemoteNotification bewirkt. Hast du meine Frage tatsächlich gelesen? Gemäß der offiziellen Dokumentation von Apple teilt didReceiveRemoteNotification dem Delegierten mit, dass die ausgeführte App eine Remote-Benachrichtigung erhalten hat. Ich frage, was ein guter Weg ist, um festzustellen, ob der Benutzer auf eine Benachrichtigung getippt hat. Der SO-Link, auf den Sie verwiesen haben, dient dazu, wenn die App von einem Neuanfang aus gestartet wird. Ich frage das Szenario, wenn sich die App im Hintergrund befindet.
Bao Lei
1
@soulshined OK, vielleicht habe ich es nicht klar genug gesagt. Ich meine, wenn die App vollständig beendet ist, nicht im Hintergrund, wird ja didFinishLaunching aufgerufen. Wenn Sie jedoch Ihre App starten und dann die App im Hintergrund ausführen und jetzt eine Benachrichtigung eingeht und der Benutzer auf die Benachrichtigung tippt, wird didFinishLaunching nicht erneut aufgerufen. Stattdessen werden applicationWillEnterForeground und applicationDidBecomeActive aufgerufen. Wie können Sie feststellen, dass die App in den Vordergrund tritt, weil der Benutzer auf die Benachrichtigung oder das App-Symbol getippt hat?
Bao Lei

Antworten:

102

OK, ich habe es endlich herausgefunden.

Wenn Sie in den Zieleinstellungen ➝ Registerkarte Funktionen ➝ Hintergrundmodi "Remote-Benachrichtigungen" aktivieren, application:didReceiveRemoteNotification:werden diese ausgelöst, sobald eine Benachrichtigung eintrifft (solange sich die App im Hintergrund befindet). In diesem Fall kann nicht festgestellt werden, ob Der Benutzer tippt auf die Benachrichtigung.

Wenn Sie dieses Kontrollkästchen deaktivieren, application:didReceiveRemoteNotification:wird es nur ausgelöst, wenn Sie auf die Benachrichtigung tippen.

Es ist ein wenig seltsam, dass das Aktivieren dieses Kontrollkästchens das Verhalten einer der App-Delegat-Methoden ändert. Wenn dieses Kontrollkästchen aktiviert ist, verwendet Apple zwei verschiedene Delegierungsmethoden für den Empfang von Benachrichtigungen und das Tippen auf Benachrichtigungen. Ich denke, die meisten Entwickler möchten immer wissen, ob eine Benachrichtigung aktiviert ist oder nicht.

Hoffentlich ist dies hilfreich für alle anderen, die auf dieses Problem stoßen. Apple hat es hier auch nicht klar dokumentiert, daher habe ich eine Weile gebraucht, um es herauszufinden.

Geben Sie hier die Bildbeschreibung ein

Bao Lei
quelle
6
Ich habe die Hintergrundmodi vollständig auf "AUS" gesetzt, werde aber trotzdem benachrichtigt, wenn eine Benachrichtigung mit der App im Hintergrundmodus eintrifft.
Gottfried
5
Toll! Das hat mir geholfen. Schade, dass ich Remote-Benachrichtigungen ausschalten muss. Ich möchte Daten vorab abrufen, wenn eine Push-Benachrichtigung eintrifft UND das Tippen des Benutzers erkennt. Ich kann keinen Weg finden, dies zu erreichen.
Peter Fennema
1
Ich stelle den Hintergrundmodus auf "EIN" und aktiviere die Fernbenachrichtigungen. Das Benachrichtigungsereignis kann immer noch nicht erkannt werden.
Kalim Sayyad
1
Ich habe das gleiche Problem Der Hintergrundmodus ist auf "EIN" gesetzt und die Remote-Benachrichtigung aktiviert, werde aber immer noch nicht benachrichtigt, wenn die Benachrichtigung im Hintergrundmodus bei der App eintrifft
Swapnil Dhotre
@Bao - Ich denke, es wird eine Ablehnung im Appstore verursachen, da diese Option im Wesentlichen zum Herunterladen von Inhalten im Zusammenhang mit Benachrichtigungen im Hintergrund verwendet wird. Wenn Sie also keine Inhalte herunterladen, kann Apple Ihre App ablehnen. developer.apple.com/library/ios/documentation/iPhone/Conceptual/…
niks
68

Ich habe nach dem gleichen Thema gesucht wie Sie und tatsächlich eine Lösung gefunden, bei der keine Remote-Benachrichtigung angekreuzt werden muss.

Um zu überprüfen, ob der Benutzer getippt hat oder sich die App im Hintergrund befindet oder aktiv ist, müssen Sie nur den Anwendungsstatus einchecken

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

    }

Weitere Informationen finden Sie unter:

UIKit Framework-Referenz> UIApplication-Klassenreferenz> UIApplicationState

Dennismus
quelle
10
Was ist mit der App UIApplicationStateInactive, wenn der Benutzer mit dem Benachrichtigungscenter (Wischen von oben) oder dem Kontrollzentrum (Wischen von unten) in Kontakt tritt, während die App auf dem Bildschirm angezeigt wird? Anscheinend kann nicht festgestellt werden, ob APNS im inaktiven Zustand empfangen wurde oder der Benutzer tatsächlich darauf getippt hat.
Kostia Kim
1
@KostiaKim Sie haben Recht, aber das ist ein super Randfall ... ansonsten ist diese Antwort die zutreffendste in Bezug auf die Trennung zwischen App im Vordergrund ... Benutzer tippen ... App im Hintergrund
Honey
Danke, diese Art von entmystifiziert alles. Denken Sie daran, dass die Push-Benachrichtigung den content-availableSchlüssel enthalten muss, damit die Delegate-Methode aufgerufen werden kann, wenn sich die App im Hintergrund befindet. Anschließend muss die Benachrichtigung lautlos sein (dh keinen Sound oder Ausweis enthalten), wie in den offiziellen Dokumenten angegeben .
Nickkk
1
@KostiaKim Ich konnte das Problem beheben, dass ich nicht wusste, ob das Kontrollzentrum geöffnet war oder ob der Benutzer tatsächlich auf die Benachrichtigung tippte, indem ich einen Booleschen Wert hinzufügte, der in der auf true und in der auf func applicationDidEnterBackground(_ application: UIApplication)false gesetzt ist. Dadurch konnte ich func applicationDidBecomeActive(_ application: UIApplication)die in-App anzeigen Benachrichtigungen, wenn die App aufgrund des Kontrollzentrums oder der Benachrichtigungsliste inaktiv ist
DatForis
3
Es verwirrt mich, warum Apple kein anderes Ereignis präsentiert oder einfach ein Flag in die Metadaten eingefügt hat, um anzuzeigen, dass der Benutzer tatsächlich mit der Benachrichtigung interagiert hat. Es ist für eine wichtige Information, die die Erwartung des Benutzers an das Anwendungsverhalten beeinflusst, ziemlich unangemessen, ableiten zu müssen, ob der Benutzer eine Aktion basierend auf Umgebungsinformationen über den Anwendungsstatus ausgeführt hat. Vielleicht müssen Sie nur eine benutzerdefinierte Aktion erstellen, um die App mit diesen Informationen zu öffnen.
Allan Nienhuis
20

Laut iOS / XCode: Woher wissen Sie, dass die App mit einem Klick auf Benachrichtigung oder auf das Sprungbrett-App-Symbol gestartet wurde? Sie müssen in didReceiveLocalNotification wie folgt nach dem Anwendungsstatus suchen:

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

Obwohl es für mich nicht ganz sinnvoll ist, scheint es zu funktionieren.

Werner Kratochwil
quelle
1
In diesem Szenario würde es nicht funktionieren. Das habe ich schon mal versucht. Wenn dieses Kontrollkästchen aktiviert wurde (Einzelheiten finden Sie in meiner akzeptierten Antwort), wird beim Eintreffen der Benachrichtigung Ihre Zeile mit dem Kommentar "// Benutzer hat Benachrichtigung getippt" eingegeben, obwohl der Benutzer nicht auf die Benachrichtigung getippt hat (die Benachrichtigung ist gerade eingetroffen) ).
Bao Lei
3
Ich bin nicht einverstanden. Ich habe "Remote-Benachrichtigungen" in meinen Hintergrundfunktionen überprüft und es funktioniert wie in meiner Antwort beschrieben. Ich habe auch "Hintergrundabruf" aktiviert. Vielleicht bewirkt das die Veränderung?
Werner Kratochwil
Könnten Sie bitte +1 meine Antwort? ;-) Ich brauche noch einige Stimmen, um mehr am Stackoverflow teilnehmen zu können. Vielen Dank
Werner Kratochwil
Ja, das ist die Lösung. tnx.
Afshin
Beachten Sie, dass dies falsch positiv ist, wenn die Benachrichtigung gesendet wird, während sich der Benutzer auf einem anderen Bildschirm befindet (z. B. wenn er die Statusleiste herunterzieht und dann eine Benachrichtigung von Ihrer App erhält).
Kevin Cooper
16

Wenn jemand es in Swift 3.0 will

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
    }

für schnell 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
quelle
Auf welchem ​​Trigger sollten wir diesen Code hinzufügen, didReceiveRemoteNotification oder didFinishLaunchingWithOptions?
Dashrath
2
Auf didReceiveRemoteNotification
Hamid Shahsavari
@Hamid sh ,,,, Ich habe in allen Zuständen eine Push-Benachrichtigung erhalten, dh wenn sich die App im Vordergrund befindet, im Hintergrund, geschlossen (beendet) ..! aber mein Problem ist, wie man die Anzahl der Ausweise erhöht, wenn sich die App im Hintergrund befindet und geschlossen (beendet) ist ???? Bitte sagen Sie mir im Detail, wie ich es mache ....? Die Anzahl meiner App-Abzeichen wird nur erhöht, wenn sich die App im Vordergrund befindet. wenn möglich bitte kurz mitteilen .......!
Kiran Jadhav
8

Wenn Sie "Hintergrundmodi"> "Remote-Benachrichtigungen" aktiviert haben == JA, tippen Sie auf Benachrichtigungsereignis, um Folgendes zu erreichen:

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

Es hat mir geholfen. Genießen Sie bitte :)

Aleks Osipov
quelle
Ich habe gesehen, dass Apple dies hinzugefügt hat, konnte aber nicht herausfinden, wie die benutzerdefinierte Nutzlast der Benachrichtigung auf diese Weise abgerufen werden kann.
Sudo
Diese Funktion wird aufgerufen, wenn sich die App im Hintergrund befindet und aktiv wird und wenn die App aus dem getöteten Zustand startet
Nikhil Muskur
@NikhilMuskur nur wenn "Remote-Benachrichtigungen" aktiviert sind, oder?
Zorayr
@ Zorayr yup das ist richtig
Nikhil Muskur
8

Ich bin auch auf dieses Problem gestoßen - aber unter iOS 11 mit dem neuen UserNotificationsFramework.

Hier ist es für mich so:

  • Neustart: application:didFinishLaunchingWithOptions:
  • Von einem Endzustand empfangen: application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • Im Vordergrund erhalten: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • Im Hintergrund erhalten: userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
Vor
quelle
Dies ist der Weg, um dies in iOS 11 +
OhadM
Das ist einfach so kompliziert 🤦‍♂️
Zorayr
4

In meinem Fall machte der Hintergrundmodus AUS keinen Unterschied. Wenn die App jedoch angehalten wurde und der Benutzer auf die Benachrichtigung tippte, konnte ich den Fall mit dieser Rückrufmethode behandeln:

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

}
DoruChidean
quelle
Dies ist der offizielle Weg, den Apple sagt. Laut dem Apple-Dokument wird es aufgerufen, wenn Benutzer mit der Push-Benachrichtigungs-Benutzeroberfläche interagieren. Wenn sich die App nicht im Hintergrund befindet, wird die App im Hintergrundmodus gestartet und diese Methode aufgerufen.
Ryan
3

Für iOS 10 und höher geben Sie dies in AppDelegate ein, um zu erfahren, dass die Benachrichtigung getippt ist (funktioniert sogar, wenn die App geschlossen oder geöffnet ist).

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
print("notification tapped here")
}
Yatin Arora
quelle
1
Dies ist die richtige Antwort für iOS 10+. Eine vollständige Erklärung finden Sie in einem anderen Thread hier: stackoverflow.com/a/41783666/1761357 .
dead_can_dance
2

Es gibt zwei Funktionen für das Handle, die PushNotification innerhalb der PushNotificationManagerKlasse erhalten:

class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate{

}

Da habe ich den ersten am Auslöser getestet, sobald die Benachrichtigung eingetroffen ist

@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([])

}

Und die zweite, wenn Sie auf Benachrichtigung tippen:

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

Ich habe es auch mit EIN- und AUS-Status der Fernbenachrichtigung getestet (im Hintergrundmodus).

Saeid
quelle
1

SWIFT 5.1

UIApplication.State hat bei mir nicht funktioniert, da nach dem Lesen des Fingerabdrucks (Modal wird angezeigt) in meiner App die Benachrichtigung auch in der oberen Leiste angezeigt wird und der Benutzer darauf klicken muss.

Ich habe erstellt

public static var isNotificationFromApp: Bool = false

in AppDelegateund ich setze es truein meinem Start viewControllerund dann in meiner Benachrichtigung storyboard/ viewControllerich überprüfe das einfach :)

Hoffe, es kann nützlich sein

Sanduhr
quelle
-7

Sie können die Nutzdaten Ihrer Push-Benachrichtigung so konfigurieren, dass die application:didReceiveRemoteNotification:fetchCompletionHandler:Methode des App- Delegaten aufgerufen wird , wenn sich die App im Hintergrund befindet. Sie können hier ein Flag setzen, damit der Benutzer beim nächsten Start Ihrer Anwendung Ihre Operation ausführen kann.

Aus der Apple-Dokumentation sollten Sie diese Methoden verwenden, um neue Inhalte im Zusammenhang mit Push-Benachrichtigungen herunterzuladen. Damit dies funktioniert, müssen Sie die Remote-Benachrichtigung aus dem Hintergrundmodus aktivieren und Ihre Push-Benachrichtigungsnutzdaten müssen einen content-availableSchlüssel mit dem Wert 1 enthalten. Weitere Informationen finden Sie unter Verwenden von Push-Benachrichtigungen zum Initiieren eines Downloads aus dem Apple-Dokument hier .

Eine andere Möglichkeit besteht darin, die Anzahl der Ausweise in der Nutzlast der Push-Benachrichtigung zu ermitteln. Wenn Ihre Anwendung das nächste Mal gestartet wird, können Sie die Anzahl der Anwendungsausweise überprüfen. Wenn es größer als Null ist, führen Sie Ihren Vorgang durch und löschen Sie die Ausweisanzahl auch vom Server.

Hoffe das hilft dir.

Ameet Dhas
quelle
@ Bhusan hast du die Details meiner Frage gelesen? Ich sehe, dass didReceiveRemoteNotification aufgerufen wird, wenn die Benachrichtigung gerade eintrifft (bevor der Benutzer darauf tippt). Ich wollte herausfinden, ob der Benutzer darauf getippt hat.
Bao Lei
Sie haben die Frage von OP nicht beantwortet. Er will nicht nur wissen, dass es eine Benachrichtigung gegeben hat. Er möchte wissen, ob der Benutzer die App durch Tippen auf diese Remote-Benachrichtigung geöffnet hat.
Joe C