Rileva se l'app è stata avviata / aperta da una notifica push


172

È possibile sapere se l'app è stata avviata / aperta da una notifica push?

Immagino che l'evento di lancio possa essere catturato qui:

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

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

    }
}

Tuttavia, come posso rilevare che è stato aperto da una notifica push quando l'app era in background?


6
Questo è un post vecchio, ma molto utile. Purtroppo le risposte migliori non risolvono effettivamente il problema (come indicano i commenti). Considera di contrassegnare una nuova risposta come "accettata" poiché quella attuale non è completa.
MobileVet

1
Questa domanda ha 100k + visualizzazioni ma la risposta selezionata è errata o completa. Per i visitatori, considera l'ordinamento per Attivo anziché per Voti per trovare soluzioni moderne.
Albert Renshaw,

Risposte:


187

Vedi questo codice:

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

uguale a

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification

19
@ManuelM. Questa è una buona risposta in quanto mostra come rilevare quando un'app in background viene portata in primo piano da una notifica push. Per quando l'app non è in esecuzione, è necessaria la risposta di M.Othman di seguito.
OpenUserX03

6
Ricevo la chiamata all'applicazione: didReceiveRemoteNotification: dopo aver toccato la notifica indipendentemente dal fatto che l'app sia solo in background o non sia in esecuzione, quindi questa risposta si adatta perfettamente alle mie esigenze. Testato su iOS 7 e 8
Newtz,

16
Come altri hanno sottolineato, questo non rileva "lanciato / aperto da una notifica push". Viene chiamato quando viene ricevuta la notifica, non quando viene aperta. Quindi, se hai ricevuto una notifica in bg ma hai toccato l'icona dell'app per aprire l'app, il codice che hai qui sarà ancora in esecuzione e potresti aprire una pagina che l'utente non intendeva aprire.
Bao Lei,

4
@ManuelM. questo metodo non indica se l'app è stata aperta tramite il centro notifiche o l'icona dell'app se sono state selezionate le modalità in background: la notifica remota è selezionata. Lo fa quando è deselezionato. Ho documentato la differenza in questo post: stackoverflow.com/questions/32061897/…
Bao Lei

2
Confermato che funziona con Google Cloud Messaging.
CularBytes,

127

in ritardo ma forse utile

Quando l'app non è in esecuzione

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

è chiamato ..

dove è necessario verificare la notifica 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");
}

2
Si noti che nel frammento di cui sopra, la notifica non deve essere dichiarata come (UILocalNotification *) ma come (NSDictionary *)
cosmix

1
In questo modo puoi vedere se c'erano delle notifiche per l'app, mentre non erano in esecuzione! La domanda era: come rilevare se l'app è stata aperta da una notifica. In questo caso viene chiamato didReceiveRemoteNotification, anche se l'app non era in esecuzione. - Mi piace la tua risposta, perché è abbastanza importante per molti casi, ma non la risposta corretta alla domanda.
Axel Zehden,

La tua risposta e questa risposta fanno entrambe la stessa cosa?
Miele,

38

Il problema che abbiamo riscontrato era l'aggiornamento corretto della vista dopo l'avvio dell'app. Ci sono sequenze complicate di metodi del ciclo di vita qui che diventano confusi.

Metodi del ciclo di vita

I nostri test per iOS 10 hanno rivelato le seguenti sequenze di metodi del ciclo di vita per i vari casi:

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

Il problema

Ok, quindi ora dobbiamo:

  1. Determina se l'utente sta aprendo l'app da un push
  2. Aggiorna la vista in base allo stato push
  3. Cancella lo stato in modo che le aperture successive non riportino l'utente nella stessa posizione.

Il punto difficile è che l'aggiornamento della vista deve avvenire quando l'applicazione diventa effettivamente attiva, che è lo stesso metodo del ciclo di vita in tutti i casi.

Schizzo della nostra soluzione

Ecco i componenti principali della nostra soluzione:

  1. Archivia una notificationUserInfovariabile di istanza su AppDelegate.
  2. Impostato notificationUserInfo = nilin entrambi applicationWillEnterForegrounde didFinishLaunchingWithOptions.
  3. Situato notificationUserInfo = userInfoindidReceiveRemoteNotification:inactive
  4. Da applicationDidBecomeActivesempre chiamare un metodo personalizzato openViewFromNotificatione passare self.notificationUserInfo. Se self.notificationUserInfoè zero, torna presto, altrimenti apri la vista in base allo stato di notifica trovato in self.notificationUserInfo.

Spiegazione

Quando si apre da un push didFinishLaunchingWithOptionso applicationWillEnterForegroundviene sempre chiamato immediatamente prima didReceiveRemoteNotification:inactive, quindi per prima cosa reimpostiamo notificationUserInfo in questi metodi in modo che non ci sia stato stantio. Quindi, se didReceiveRemoteNotification:inactiveviene chiamato, sappiamo che ci stiamo aprendo da un push, quindi impostiamo il self.notificationUserInfoquale viene quindi raccolto applicationDidBecomeActiveper inoltrare l'utente alla vista corretta.

Esiste un caso finale che si verifica se l'utente ha l'app aperta all'interno del commutatore di app (ovvero toccando due volte il pulsante Home mentre l'app è in primo piano) e quindi riceve una notifica push. In questo caso didReceiveRemoteNotification:inactiveviene chiamato solo e né WillEnterForeground né didFinishLaunching vengono chiamati, quindi è necessario uno stato speciale per gestire quel caso.

Spero che questo ti aiuti.


Finalmente qualcosa che funziona, grazie! Volevo creare un flag "appResuming" e aprire lo schermo nei receivemetodi quando lo stato dell'app è attivo o l'app sta riprendendo. Ciò potrebbe causare problemi con la modifica dei VC quando l'app è ancora inattiva. La tua soluzione sembra eccezionale, fino a quando Apple non cambierà nuovamente il ciclo di vita.
shelll

Che dire di iOS 9, i metodi del ciclo di vita sono chiamati allo stesso modo e ordine? Non ho già dispositivi iOS 9, quindi non posso testarlo correttamente.
shelll

2
Esistono altri due casi limite tranne il selettore di app. 1) Quando il centro notifiche viene estratto dalla parte superiore e si sovrappone all'app 2) Quando il pannello iOS con wifi / BT / ecc. Viene estratto dalla parte inferiore e sovrappone l'app. In tutti e tre i casi applicationWillResignActiveviene chiamato solo il e quindi il applicationDidBecomeActive. Quindi, dopo che applicationWillResignActiveviene chiamato, non salvare la notifica ricevuta finché non viene chiamato applicationDidEnterBackgroundo applicationDidBecomeActive.
shelll

Grazie per aver aggiunto questi casi @shelll. Diventa sempre più complicato! Non sono sicuro di iOS9. Direi che probabilmente è sicuro supporre che siano uguali, ma chi lo sa.
Eric Conner,

Solo un avviso. Oggi stavo testando iOS 11 Beta 9 e ho scoperto che nel caso in cui tu abbia la tua app in primo piano, blocchi il telefono e quindi selezioni una notifica push dalla schermata di blocco, sta chiamando didReceiveRemoteNotification: background prima di chiamare applicationWillEnterForeground anziché quello che stiamo vedendo su iOS 10 dove chiama applicationWillEnterForeground e poi didReceiveRemoteNotification: inattivo - quindi questo è un caso limite non ancora coperto. A mio avviso si tratta di un bug nel codice iOS, ma data la vicinanza della versione iOS 11, è qualcosa di cui essere consapevoli.
Roy

24

Questo è un post ben usurato ... ma manca ancora una soluzione effettiva al problema (come sottolineato nei vari commenti).

La domanda originale riguarda il rilevamento di quando l'app è stata avviata / aperta da una notifica push, ad esempio un utente tocca la notifica. Nessuna delle risposte copre effettivamente questo caso.

Il motivo può essere visto nel flusso di chiamate quando arriva una notifica, application:didReceiveRemoteNotification...

viene chiamato quando viene ricevuta la notifica E di nuovo quando la notifica viene toccata dall'utente. Per questo UIApplicationStatemotivo , non si può dire semplicemente guardando se l'utente lo ha toccato.

Inoltre, non è più necessario gestire la situazione di un "avvio a freddo" dell'app in application:didFinishLaunchingWithOptions...quanto application:didReceiveRemoteNotification...viene richiamata dopo il lancio in iOS 9+ (forse anche 8).

Quindi, come puoi sapere se l'utente tocca ha avviato la catena di eventi? La mia soluzione è contrassegnare l'ora in cui l'app inizia a uscire dallo sfondo o all'avvio a freddo e quindi a controllare tale orario application:didReceiveRemoteNotification.... Se è inferiore a 0,1 s, allora puoi essere abbastanza sicuro che il tocco abbia attivato l'avvio.

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

Ho provato questo per entrambi i casi (app in background, app non in esecuzione) su iOS 9+ e funziona come un fascino. 0.1s è anche abbastanza conservativo, il valore reale è ~ 0.002s quindi anche 0.01 va bene.


1
Questa sembra essere l'unica soluzione funzionante che distingue tra il tocco effettivo della notifica e l'apertura della barra di stato sull'app.
liviucmg,

4
Questa è l'unica soluzione funzionante di tutto StackOverflow. L'unica cosa che vorrei aggiungere è che quando supporti iOS 10 e versioni successive, puoi semplicemente utilizzare l' UNNotificationCenterAPI, in particolare i metodi UNNotificationCenterDelegate. Quella API chiama il userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) metodo func solo quando l'utente ha effettivamente toccato la notifica.
DenHeadless,

come cerca swift 3?
Jochen Österreicher,

La soluzione non funziona mentre un'app è in stato inattivo (l'utente passa al centro notifiche o al centro controllo) e riceve una notifica. Quando l'utente tocca la notifica, l'app non riceve la applicationWillEnterForeground chiamata, di conseguenza, la soluzione non riesce a rilevare il tocco.
DevGansta,

@DevGansta Quando aggiungi la tua classe come UNUserNotificationCenter.current().delegatein application:didFinishLaunchingWithOptions, l'app chiamerà userNotificationCenter(didReceive response)dopo il tocco nel caso che hai descritto
Dorian Roy

22

Quando l'app viene terminata e l'utente tocca sulla notifica push

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

Quando l'app è in background e l'utente tocca la notifica push

Se l'utente apre l'app dall'avviso visualizzato dal sistema, il sistema potrebbe richiamare nuovamente questo metodo quando l'app sta per entrare in primo piano in modo da poter aggiornare l'interfaccia utente e visualizzare le informazioni relative alla notifica.

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

A seconda della tua app, può anche inviarti push silenziosi con l' content-availableinterno aps, quindi fai attenzione anche a questo :) Vedi https://stackoverflow.com/a/33778990/1418457


2
Solo una risposta che non sembra un trucco sporco e corretto. Quello che mi manca è se l'app è in background e l'utente la apre manualmente, come verificarlo? Pur essendo in grado di verificare l'avvio a freddo e la spinta dallo sfondo.
Jochen Österreicher,

1
@ JochenÖsterreicher Ciao, lo riassumo qui, per favore controlla medium.com/@onmyway133/…
onmyway133

19

Swift 2.0 per stato "Non in esecuzione" (notifica locale e 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
}

15

Nel application:didReceiveRemoteNotification:controllo se avete ricevuto la notifica quando la vostra applicazione è in primo piano o sullo sfondo.

Se è stato ricevuto in background, avvia l'app dalla notifica.

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        NSLog(@"Notification received by running app");
    } else {
        NSLog(@"App opened from Notification");
    }
}

3
Tieni presente che "App aperta da Notifica" sarà un falso positivo se la notifica viene inviata mentre l'utente si trova su una schermata diversa (ad esempio, se aprono la barra di stato e ricevono una notifica dalla tua app).
Kevin Cooper,

4
@Kevin Exactly. Ti viene da chiedersi perché Apple apparentemente abbia messo un stagista per progettare il processo di gestione delle notifiche ...
Andreas,

come possiamo rilevare se tocchiamo la notifica ricevuta nello stato attivo
Mayank Jain,

13

Per rapido:

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

    }

}

4

Sì, puoi rilevare con questo metodo in appDelegate :

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

Per la notifica locale:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}

1
Questo metodo non viene chiamato se l'app non è in esecuzione. Questo è ciò che è stato chiesto qui
Pfitz

Il mio problema non è gestire la notifica, ma piuttosto sapere se è stata aperta quando si fa clic sul banner (quando l'app è in background).
joao

3

se qualcuno vuole la risposta in rapido 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
    }
}

ma come sapere se l'applicazione viene aperta toccando la notifica push al termine dell'app
user3804063

1
quando qualcuno tocca il push, l'app sarà aperta, indipendentemente dal fatto che sia stata chiusa o meno. e il caso .inactive sta chiamando
Hamid Shahsavari

1
ho bisogno di rilevare se l'app viene aperta toccando la pressione e vuole navigare ai rispettivi contenuti che ho visto instagram fare quello
user3804063

Che ne dite di notifiche locali?
Amir Shabani,

3

Pubblicando questo per gli utenti Xamarin.

La chiave per rilevare se l'app è stata avviata tramite una notifica push è il AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)metodo e il dizionario delle opzioni passato.

Il dizionario opzioni avrà questa chiave in esso se si tratta di una notifica locale: UIApplication.LaunchOptionsLocalNotificationKey.

Se è una notifica remota, lo sarà UIApplication.LaunchOptionsRemoteNotificationKey.

Quando la chiave è LaunchOptionsLocalNotificationKey, l'oggetto è di tipo UILocalNotification. È quindi possibile guardare la notifica e determinare quale notifica specifica è.

Suggerimento professionale: UILocalNotificationnon ha un identificatore, allo stesso modo UNNotificationRequest. Inserisci una chiave del dizionario in UserInfo contenente un requestId in modo che durante il test di UILocalNotification, avrai una richiestaId specifica disponibile su cui basare un po 'di logica.

Ho scoperto che anche su dispositivi iOS 10+ che durante la creazione di notifiche di posizione utilizzando il UNUserNotificationCenter 's AddNotificationRequest& UNMutableNotificationContent, che quando l'app non è in esecuzione (l'ho uccisa) e viene avviato toccando la notifica nel centro di notifica, il dizionario contiene ancora l' UILocalNotificaitonoggetto.

Ciò significa che il mio codice che verifica il lancio basato sulle notifiche funzionerà su dispositivi 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;
}

2

Direttamente dalla documentazione per

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

Se l'app è in esecuzione e riceve una notifica remota, l'app chiama questo metodo per elaborare la notifica.

L'implementazione di questo metodo dovrebbe utilizzare la notifica per intraprendere una serie di azioni appropriate.

E un po 'più tardi

Se l'app non è in esecuzione quando arriva una notifica push, il metodo avvia l'app e fornisce le informazioni appropriate nel dizionario delle opzioni di avvio.

L'app non chiama questo metodo per gestire tale notifica push.

Invece, la tua implementazione di

application:willFinishLaunchingWithOptions:

o

application:didFinishLaunchingWithOptions:

Il metodo deve ottenere i dati del payload di notifica push e rispondere in modo appropriato.


2

Inizierò con un diagramma di stato che ho creato per uso personale per visualizzarlo in modo più accurato e per prendere in considerazione tutti gli altri stati: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeZ3ZHZmZZmUZdccz ? gid = 0 & singolo = true

Utilizzando questo grafico, possiamo vedere ciò che è effettivamente necessario per sviluppare un solido sistema di gestione delle notifiche che funziona in quasi tutti i possibili casi d'uso.

Soluzione completa ↓

  • Memorizzare il payload delle notifiche in didReceiveRemoteNotification
  • Cancella le notifiche memorizzate in applicationWillEnterForeground e didFinishLaunchingWithOptions
  • Per affrontare i casi in cui è stato estratto il Centro di controllo / Centro di notifica, puoi utilizzare un flag willResignActiveCalled e impostarlo inizialmente su false , Impostalo su true nel metodo applicationWillResignActive ,
  • Nel metodo didReceiveRemoteNotification , salvare le notifiche (userInfo) solo quando willResignActiveCalled è falso.
  • Reimposta willResignActiveCalled su false nel metodo applicationDidEnterBackground e applicationDidBecomeActive .

Nota: una risposta simile è suggerita nei commenti sulla risposta di Eric, tuttavia il foglio di stato aiuta a trovare tutti i possibili scenari come ho fatto nella mia app.

Si prega di trovare il codice completo di seguito e di commentare di seguito se non viene gestito alcun caso specifico:

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 : qui è possibile scrivere tutto il codice per navigare in diverse parti dell'applicazione, gestire database (CoreData / Realm) e fare tutte le altre cose che devono essere fatte quando si riceve una notifica.

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

meglio mettere questo come un commento poiché questa non è la risposta.
Maddy,

@ Maddy Grazie per il suggerimento, aggiornata la risposta con tutti i dettagli
chetan an e

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

1

Esiste un solo modo affidabile e funziona solo per iOS 10+ :

Utilizzando il metodo UNUserNotificationCenterdell'attrezzo 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
    }
    ...
}

0

Puoi usare:

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

per gestire le notifiche push remote.

Controlla qui la documentazione



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

E se l'app fosse chiusa (terminata). Come Twitter o Instagram, in qualche modo lo rileva e se l'app viene persino chiusa, ti reindirizza a nuovi post o immagini o al tuo profilo, ecc.
Tarvo Mäesepp,

0

Il problema con questa domanda è che "l'apertura" dell'app non è ben definita. Un'app viene avviata a freddo da uno stato non in esecuzione oppure viene riattivata da uno stato inattivo (ad es. Dal passaggio a un'altra da un'altra app). Ecco la mia soluzione per distinguere tutti questi possibili stati:

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

Ed MXDefaultsè solo un piccolo involucro per NSUserDefaults.


0

Per 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)")

    }
}

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

0

Per iOS 10+, puoi utilizzare questo metodo per sapere quando si fa clic sulla notifica indipendentemente dallo stato dell'app.

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

    //Notification clicked
    completionHandler()
}

0

La risposta di M.Othman è corretta per le app che non contengono delegato di scena Per le app delegate di scena Questo ha funzionato per me su iOS 13

Ecco il codice per quello che dovrebbe essere scritto in connetterà la scena

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

Codice per il delegato dell'app per supportare le versioni precedenti didFinishLaunchingWithOptions

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

            //Launched from push notification
        } else {

            //Launch from other source
        }

-1

Per utenti veloci:

Se vuoi lanciare una pagina diversa all'apertura da push o qualcosa del genere, devi controllarla didFinishLaunchingWithOptionscome:

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

Il delegato non ha membri navigationController
Pablo Cegarra

1
Creare un controller di navigazione nel file AppDelegate.h. Lo sto usando e funziona!
AAA,

-1

IN SWIFT:

Sto eseguendo le notifiche push (con recupero in background). Quando la mia app è in background e ricevo una notifica push, ho scoperto che didReceiveRemoteNotification in appDelegate sarebbe stata chiamata due volte; una volta per quando viene ricevuta la notifica e un'altra quando l'utente fa clic sull'avviso di notifica.

Per rilevare se è stato fatto clic sull'avviso di notifica, basta controllare se applicationState raw value == 1 in didReceiveRemoteNotification in 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
    }
}

Spero che aiuti.


-1

Quando l'app è in background come Shanegao , puoi usarla

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

Ma se vuoi avviare l'applicazione e quando l'app è chiusa e vuoi eseguire il debug dell'applicazione, puoi andare su Modifica schema e nel menu a sinistra selezionare Esegui e quindi in avvio selezionare Attendi l' avvio dell'eseguibile e quindi l'avvio dell'applicazione quando fai clic su notifica push

Modifica schema> Esegui> Attendi l'avvio dell'eseguibile

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.