Wykryj, czy aplikacja została uruchomiona / otwarta z powiadomienia push

172

Czy można dowiedzieć się, czy aplikacja została uruchomiona / otwarta z powiadomienia push?

Wydaje mi się, że wydarzenie inauguracyjne można złapać tutaj:

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

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

    }
}

Jak jednak mogę wykryć, że została otwarta z powiadomienia push, gdy aplikacja była w tle?

joao
źródło
6
To stary, ale bardzo przydatny post. Niestety najlepsze odpowiedzi nie rozwiązują problemu (jak wskazują komentarze). Rozważ oznaczenie nowej odpowiedzi jako „zaakceptowanej”, ponieważ obecna nie jest kompletna.
MobileVet
1
To pytanie ma ponad 100 000 wyświetleń, ale wybrana odpowiedź jest niepoprawna lub kompletna. Odwiedzający powinni rozważyć sortowanie według aktywnych, a nie według głosów, aby znaleźć nowoczesne rozwiązania.
Albert Renshaw

Odpowiedzi:

187

Zobacz ten kod:

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

taki sam jak

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
shanegao
źródło
19
@ManuelM. To dobra odpowiedź, ponieważ pokazuje, jak wykryć, kiedy aplikacja działająca w tle jest przenoszona na pierwszy plan z powiadomienia push. Gdy aplikacja nie działa, potrzebujesz odpowiedzi M.Othmana poniżej.
OpenUserX03
6
Otrzymuję wezwanie do aplikacji: didReceiveRemoteNotification: po dotknięciu powiadomienia niezależnie od tego, czy aplikacja działa tylko w tle, czy w ogóle nie działa, więc ta odpowiedź idealnie odpowiada moim potrzebom. Testowane na iOS 7 i 8
Newtz
16
Jak niektórzy wskazywali, nie wykrywa to „uruchomionego / otwartego z powiadomienia push”. Jest to wywoływane po odebraniu powiadomienia, a nie po jego otwarciu. Jeśli więc otrzymałeś powiadomienie w bg, ale stuknąłeś ikonę aplikacji, aby otworzyć aplikację, kod, który tutaj masz, nadal będzie działać i możesz otworzyć stronę, której użytkownik nie zamierzał otworzyć.
Bao Lei
4
@ManuelM. ta metoda nie określa, czy aplikacja została otwarta za pośrednictwem centrum powiadomień, czy ikony aplikacji, jeśli tryby tła - sprawdzane jest zdalne powiadamianie. Działa, gdy nie jest zaznaczone. Udokumentowałem różnicę w tym poście: stackoverflow.com/questions/32061897/…
Bao Lei
2
Potwierdzono, że działa to z Google Cloud Messaging.
CularBytes
127

późno, ale może przydatne

Gdy aplikacja nie działa

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

jest nazywany ..

gdzie musisz sprawdzić powiadomienia 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");
}
Człowiek ćma
źródło
2
Zauważ, że w powyższym fragmencie powiadomienie nie powinno być zadeklarowane jako (UILocalNotification *), ale (NSDictionary *)
cosmix
1
W ten sposób możesz zobaczyć, czy były jakieś powiadomienia dla aplikacji, gdy nie jest uruchomiona! Pytanie brzmiało, jak wykryć, czy aplikacja została otwarta z powiadomienia. W tym przypadku wywoływana jest didReceiveRemoteNotification, nawet jeśli aplikacja w ogóle nie była uruchomiona. - Podoba mi się twoja odpowiedź, bo w wielu przypadkach jest dość ważna, ale nie jest to poprawna odpowiedź na pytanie.
Axel Zehden
Czy twoja odpowiedź i ta odpowiedź robią to samo?
Honey
38

Problem, który napotkaliśmy, polegał na poprawnym aktualizowaniu widoku po uruchomieniu aplikacji. Istnieją tu skomplikowane sekwencje metod cyklu życia, które są mylące.

Metody cyklu życia

Nasze testy na iOS 10 ujawniły następujące sekwencje metod cyklu życia dla różnych przypadków:

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

Problem

Ok, więc teraz musimy:

  1. Sprawdź, czy użytkownik otwiera aplikację po naciśnięciu
  2. Zaktualizuj widok na podstawie stanu wypychania
  3. Wyczyść stan, aby kolejne otwarcia nie powodowały powrotu użytkownika do tej samej pozycji.

Problem polega na tym, że aktualizacja widoku musi nastąpić, gdy aplikacja faktycznie staje się aktywna, co jest tą samą metodą cyklu życia we wszystkich przypadkach.

Szkic naszego rozwiązania

Oto główne elementy naszego rozwiązania:

  1. Przechowuj notificationUserInfozmienną wystąpienia w AppDelegate.
  2. Ustaw notificationUserInfo = nilw obu applicationWillEnterForegroundi didFinishLaunchingWithOptions.
  3. Ustaw notificationUserInfo = userInfowdidReceiveRemoteNotification:inactive
  4. Od applicationDidBecomeActivezawsze wywołuj niestandardową metodę openViewFromNotificationi podaj self.notificationUserInfo. Jeśli self.notificationUserInfojest nil, wróć wcześniej, w przeciwnym razie otwórz widok na podstawie stanu powiadomienia znalezionego w self.notificationUserInfo.

Wyjaśnienie

Podczas otwierania z wypychania didFinishLaunchingWithOptionslub applicationWillEnterForegroundjest zawsze wywoływana bezpośrednio przed didReceiveRemoteNotification:inactive, więc najpierw resetujemy notificationUserInfo w tych metodach, aby nie było przestarzałego stanu. Następnie, jeśli didReceiveRemoteNotification:inactivezostanie wywołany, wiemy, że otwieramy z push, więc ustawiamy, self.notificationUserInfoktóry jest następnie odbierany, applicationDidBecomeActiveaby przekierować użytkownika do właściwego widoku.

Jest jeszcze jeden przypadek, w którym użytkownik ma otwartą aplikację w przełączniku aplikacji (tj. Dwukrotne dotknięcie przycisku strony głównej, gdy aplikacja jest na pierwszym planie), a następnie otrzymuje powiadomienie push. Tylko w tym przypadku didReceiveRemoteNotification:inactivejest wywoływana i ani WillEnterForeground, ani didFinishLaunching nie są wywoływane, więc do obsługi tego przypadku potrzebny jest specjalny stan.

Mam nadzieję że to pomoże.

Eric Conner
źródło
Wreszcie coś, co działa, dzięki! Chciałem utworzyć flagę „appResuming” i otworzyć ekran w receivemetodach, gdy stan aplikacji jest aktywny lub aplikacja jest wznawiana. Może to prowadzić do problemów ze zmianą VC, gdy aplikacja jest nadal nieaktywna. Twoje rozwiązanie wygląda świetnie, dopóki Apple ponownie nie zmieni cyklu życia.
shelll
A co z iOS 9, czy metody cyklu życia są wywoływane w ten sam sposób i kolejność? Nie mam już urządzeń z systemem iOS 9, więc nie mogę tego poprawnie przetestować.
shelll
2
Oprócz przełącznika aplikacji istnieją jeszcze dwa przypadki skrajne. 1) Gdy centrum powiadomień jest wyciągane od góry i nakłada aplikację 2) Gdy panel iOS z Wi-Fi / BT / itp. Jest ściągany od dołu i nakłada aplikację. We wszystkich trzech przypadkach applicationWillResignActivewywoływany jest tylko element, a następnie applicationDidBecomeActive. Dlatego po applicationWillResignActivewywołaniu nie zapisuj otrzymanego powiadomienia do momentu wywołania applicationDidEnterBackgroundlub applicationDidBecomeActive.
shelll
Dziękujemy za dodanie tych przypadków @shelll. To zawsze staje się bardziej skomplikowane! Nie jestem pewien co do iOS9. Powiedziałbym, że prawdopodobnie bezpiecznie jest założyć, że są takie same, ale kto wie.
Eric Conner
Tylko jedno ostrzeżenie. Testowałem dziś iOS 11 Beta 9 i stwierdziłem, że w przypadku, gdy masz swoją aplikację na pierwszym planie, zablokuj telefon, a następnie wybierz powiadomienie push z ekranu blokady, wywołuje ona didReceiveRemoteNotification: background tuż przed wywołaniem applicationWillEnterForeground zamiast to, co widzimy na iOS 10, gdzie wywołuje applicationWillEnterForeground, a następnie didReceiveRemoteNotification: inactive - więc jest to przypadek skrajny, który nie został jeszcze omówiony. Moim zdaniem jest to błąd w kodzie iOS, ale biorąc pod uwagę, jak blisko jest wydanie iOS 11, jest to coś, o czym należy pamiętać.
Roy
24

To bardzo wysłużony post ... ale wciąż brakuje w nim rzeczywistego rozwiązania problemu (jak wskazano w różnych komentarzach).

Pierwotne pytanie dotyczyło wykrycia, kiedy aplikacja została uruchomiona / otwarta z powiadomienia push, np. Użytkownik stuka w powiadomienie. Żadna z odpowiedzi tak naprawdę nie dotyczy tego przypadku.

Powód można zobaczyć w przebiegu połączenia, gdy nadejdzie powiadomienie, application:didReceiveRemoteNotification...

zostaje wywołany, gdy powiadomienie zostanie odebrane ORAZ ponownie, gdy powiadomienie zostanie dotknięte przez użytkownika. Z tego powodu nie można tego stwierdzić, patrząc po prostu, UIApplicationStateczy użytkownik ją stuknął.

Ponadto nie musisz już zajmować się sytuacją `` zimnego startu '' aplikacji, application:didFinishLaunchingWithOptions...jak application:didReceiveRemoteNotification...to się nazywa ponownie po uruchomieniu w iOS 9+ (może również 8).

Jak więc możesz stwierdzić, czy użytkownik kliknął, rozpoczął łańcuch zdarzeń? Moim rozwiązaniem jest zaznaczenie czasu, w którym aplikacja zaczyna wychodzić z tła lub zimnego startu, a następnie sprawdzenie tego czasu application:didReceiveRemoteNotification.... Jeśli jest krótszy niż 0,1 s, możesz być całkiem pewien, że dotknięcie spowodowało uruchomienie.

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

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

Przetestowałem to w obu przypadkach (aplikacja w tle, aplikacja nie działa) na iOS 9+ i działa jak urok. 0,1 s jest również dość konserwatywne, rzeczywista wartość to ~ 0,002 s, więc 0,01 również jest w porządku.

MobileVet
źródło
1
Wydaje się, że jest to jedyne działające rozwiązanie, które odróżnia faktyczne dotknięcie powiadomienia od otwarcia paska stanu nad aplikacją.
liviucmg
4
To jedyne działające rozwiązanie ze wszystkich StackOverflow. Jedyne, co chciałbym dodać, to jeśli obsługujesz iOS 10 i nowsze UNNotificationCenterwersje , możesz po prostu użyć API, w szczególności metod UNNotificationCenterDelegate. Te API wywołują userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) metodę func tylko wtedy, gdy użytkownik faktycznie dotknął powiadomienia.
DenHeadless
jak to wygląda w przypadku szybkiej 3?
Jochen Österreicher
Rozwiązanie nie działa, gdy aplikacja jest nieaktywna (użytkownik przesuwa w dół centrum powiadomień lub przesuwa w górę centrum sterowania) i otrzymuje powiadomienie. Gdy użytkownik stuka w powiadomienie, aplikacja nie odbiera applicationWillEnterForeground połączenia, w rezultacie rozwiązanie nie wykrywa dotknięcia.
DevGansta
@DevGansta Gdy dodasz swoją klasę jak UNUserNotificationCenter.current().delegatew application:didFinishLaunchingWithOptions, aplikacja zadzwoni userNotificationCenter(didReceive response)po dotknięciu w opisanym przypadku
Dorian Roy
22

Gdy aplikacja zostanie zamknięta, a użytkownik kliknie powiadomienie push

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

Gdy aplikacja działa w tle, a użytkownik dotyka powiadomienia push

Jeśli użytkownik otworzy aplikację z poziomu alertu wyświetlanego przez system, system może ponownie wywołać tę metodę, gdy aplikacja ma zamiar wejść na pierwszy plan , aby można było zaktualizować interfejs użytkownika i wyświetlić informacje dotyczące powiadomienia.

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

W zależności od Twojej aplikacji może również wysłać Ci cichy push z content-availablewnętrzem aps, więc pamiętaj również o tym :) Zobacz https://stackoverflow.com/a/33778990/1418457

onmyway133
źródło
2
Tylko odpowiedź, która nie wydaje się brudnym hackem i poprawna. Brakuje mi tego, czy aplikacja działa w tle, a użytkownik otwiera ją ręcznie, jak to sprawdzić? Nadal można sprawdzić, czy nie ma zimnego startu w trybie push i w tle.
Jochen Österreicher
1
@ JochenÖsterreicher Cześć, podsumowuję to tutaj, sprawdź medium.com/@onmyway133/…
onmyway133
19

Swift 2.0 dla stanu `` nie działa '' (powiadomienie lokalne i zdalne)

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
źródło
15

W application:didReceiveRemoteNotification:sprawdzić, czy otrzymaliśmy powiadomienie, gdy aplikacja jest na pierwszym planie lub w tle.

Jeśli został odebrany w tle, uruchom aplikację z poziomu powiadomienia.

-(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
źródło
3
Zwróć uwagę, że „Aplikacja otwarta z powiadomienia” będzie fałszywie pozytywna, jeśli powiadomienie zostanie wysłane, gdy użytkownik znajduje się na innym ekranie (na przykład, jeśli opuści pasek stanu, a następnie otrzyma powiadomienie z Twojej aplikacji).
Kevin Cooper
4
@Kevin Exactly. To sprawia, że ​​zastanawiasz się, dlaczego Apple pozornie zlecił stażyście zaprojektowanie procesu obsługi powiadomień ...
Andreas
jak możemy wykryć, czy stukamy w powiadomienie otrzymane w stanie aktywnym
Mayank Jain,
13

Dla szybkiego:

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

    }

}
LondonGuy
źródło
4

Tak, możesz wykryć tą metodą w appDelegate :

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

W przypadku powiadomienia lokalnego:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}
Niemożliwy
źródło
1
Ta metoda nie jest wywoływana, jeśli aplikacja nie jest uruchomiona. O to pytano tutaj
Pfitz
Moim problemem nie jest obsługa powiadomienia, a raczej wiedza, czy zostało otwarte po kliknięciu banera (gdy aplikacja działa w tle).
joao
3

jeśli ktoś chce uzyskać szybką odpowiedź 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
źródło
ale jak
sprawdzić
1
gdy ktoś naciśnie przycisk push, aplikacja zostanie otwarta, niezależnie od tego, czy została zakończona, czy nie. a sprawa .inactive dzwoni
Hamid Shahsavari
1
Muszę wykryć, czy aplikacja jest otwarta, naciskając przycisk push i chce przejść do odpowiedniej treści. Widziałem, jak robi to
Instagram
A co z powiadomieniami lokalnymi?
Amir Shabani,
3

Publikowanie tego dla użytkowników platformy Xamarin.

Kluczem do wykrycia, czy aplikacja została uruchomiona za pośrednictwem powiadomienia wypychanego, jest AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)metoda i słownik opcji, który jest przekazywany.

Słownik opcje będą mieć ten klucz w nim, czy jest to lokalny powiadomienie: UIApplication.LaunchOptionsLocalNotificationKey.

Jeśli jest to zdalne powiadomienie, będzie UIApplication.LaunchOptionsRemoteNotificationKey.

Gdy klucz jest LaunchOptionsLocalNotificationKey, obiekt jest typu UILocalNotification. Następnie możesz spojrzeć na powiadomienie i określić, które to jest konkretne powiadomienie.

Porada od specjalistów: UILocalNotificationnie zawiera identyfikatora, tak samo jak UNNotificationRequest. Umieść klucz słownika w UserInfo zawierającym requestId, aby podczas testowania UILocalNotificationbył dostępny określony requestId, na którym można oprzeć pewną logikę.

Zauważyłem, że nawet na urządzeniach iOS 10+, które podczas tworzenia powiadomień o lokalizacji za pomocą UNUserNotificationCenter's AddNotificationRequest& UNMutableNotificationContent, że gdy aplikacja nie działa (zabiłem ją) i jest uruchamiana przez dotknięcie powiadomienia w centrum powiadomień, że słownik nadal zawiera UILocalNotificaitonprzedmiot.

Oznacza to, że mój kod sprawdzający uruchamianie na podstawie powiadomień będzie działał na urządzeniach z systemem iOS 8 i iOS 10+

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

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

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

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

Prosto z dokumentacji dla

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

Jeśli aplikacja działa i odbiera zdalne powiadomienie, wywołuje tę metodę w celu przetworzenia powiadomienia.

Twoja implementacja tej metody powinna korzystać z powiadomienia, aby podjąć odpowiednie działania.

I trochę później

Jeśli aplikacja nie działa w momencie nadejścia powiadomienia wypychanego, metoda uruchamia aplikację i udostępnia odpowiednie informacje w słowniku opcji uruchamiania.

Aplikacja nie wywołuje tej metody w celu obsługi tego powiadomienia wypychanego.

Zamiast tego Twoja implementacja

application:willFinishLaunchingWithOptions:

lub

application:didFinishLaunchingWithOptions:

musi pobrać dane ładunku powiadomienia wypychanego i odpowiednio zareagować.

Pfitz
źródło
2

Zacznę od wykresu stanu, który utworzyłem na własny użytek, aby go dokładniej zwizualizować i wziąć pod uwagę wszystkie inne stany: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkU3iubLg31g ? gid = 0 & single = true

Korzystając z tego wykresu, możemy zobaczyć, co jest faktycznie wymagane, aby opracować solidny system obsługi powiadomień, który działa w prawie wszystkich możliwych przypadkach użycia.

Kompletne rozwiązanie ↓

  • Przechowuj ładunek powiadomienia w didReceiveRemoteNotification
  • Wyczyść zapisane powiadomienie w applicationWillEnterForeground i didFinishLaunchingWithOptions
  • Aby poradzić sobie z przypadkami, w których Centrum sterowania / Centrum powiadomień zostało wyciągnięte, możesz użyć flagi willResignActiveCalled i początkowo ustawić ją na false , Ustaw to na true w metodzie applicationWillResignActive ,
  • W metodzie didReceiveRemoteNotification zapisuj powiadomienia (userInfo) tylko wtedy, gdy willResignActiveCalled ma wartość false.
  • Zresetuj willResignActiveCalled na false w metodzie applicationDidEnterBackground i applicationDidBecomeActive .

Uwaga: Podobna odpowiedź jest sugerowana w komentarzach do odpowiedzi Erica, jednak arkusz stanu pomaga w znalezieniu wszystkich możliwych scenariuszy, tak jak w mojej aplikacji.

Proszę znaleźć pełny kod poniżej i komentarz poniżej, jeśli jakikolwiek konkretny przypadek nie jest obsługiwany:

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 : Tutaj możesz napisać cały kod, aby przejść do różnych części aplikacji, obsługiwać bazy danych (CoreData / Realm) i wykonać wszystkie inne czynności, które należy wykonać po otrzymaniu powiadomienia.

   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
źródło
lepiej umieść to jako komentarz, ponieważ to nie jest odpowiedź.
Maddy,
@Maddy Dzięki za sugestię, zaktualizowałem odpowiedź o wszystkie szczegóły
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)
        }
    }
}
Hardik Bar
źródło
1

Jest tylko jeden niezawodny sposób i działa tylko na iOS 10+ :

Za pomocą metody UNUserNotificationCenterimplementacji 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
źródło
0

Możesz użyć:

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

do obsługi zdalnych powiadomień push.

Sprawdź tutaj dokumentację

szczęśliwy
źródło
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
źródło
Ale co, jeśli aplikacja została zamknięta (zakończona). Podobnie jak Twitter czy Instagram, w jakiś sposób to wykrywa, a jeśli aplikacja jest nawet zamknięta, przekierowuje cię do nowego posta, zdjęć lub twojego profilu itp.
Tarvo Mäesepp
0

Problem z tym pytaniem polega na tym, że „otwieranie” aplikacji nie jest dobrze zdefiniowane. Aplikacja jest uruchamiana na zimno ze stanu niedziałającego lub reaktywowana ze stanu nieaktywnego (np. W wyniku przełączenia z powrotem z innej aplikacji). Oto moje rozwiązanie, aby rozróżnić wszystkie te możliwe stany:

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

I MXDefaultsjest tylko małym opakowaniem NSUserDefaults.

skensell
źródło
0

Dla 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
źródło
0

Xcode 10 Swift 4.2.0 Łatka

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
źródło
0

W przypadku iOS 10+ możesz użyć tej metody, aby dowiedzieć się, kiedy kliknięto powiadomienie, niezależnie od stanu aplikacji.

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

    //Notification clicked
    completionHandler()
}
manishsharma93
źródło
0

Odpowiedź M.Othmana jest poprawna dla aplikacji, które nie zawierają delegata sceny Dla aplikacji delegata sceny To działało dla mnie na iOS 13

Oto kod, który należy zapisać w will connect scene

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

Kod delegata aplikacji do obsługi wcześniejszych wersji didFinishLaunchingWithOptions

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

            //Launched from push notification
        } else {

            //Launch from other source
        }
Noman Haroon
źródło
-1

Dla szybkich użytkowników:

Jeśli chcesz uruchomić inną stronę podczas otwierania z wypychania lub czegoś podobnego, musisz to sprawdzić w następujący sposób didFinishLaunchingWithOptions:

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
źródło
Delegat nie ma nawigacji członków.
Kontroler
1
Utwórz kontroler nawigacji w pliku AppDelegate.h. Używam go i działa!
AAA
-1

W SWIFT:

Uruchamiam powiadomienia push (z pobieraniem w tle). Kiedy moja aplikacja działa w tle i otrzymuję powiadomienie push, stwierdziłem, że didReceiveRemoteNotification w appDelegate zostanie wywołane dwukrotnie; raz, gdy otrzymano powiadomienie, a drugi, gdy użytkownik kliknie alert powiadomienia.

Aby wykryć, czy kliknięto alert powiadomienia, po prostu sprawdź, czy applicationState surowa wartość == 1 wewnątrz didReceiveRemoteNotification w 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
    }
}

Mam nadzieję, że to pomoże.

Johnny5
źródło
-1

Gdy aplikacja jest w tle , możesz użyć shanegao

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

Ale jeśli chcesz uruchomić aplikację i gdy aplikacja jest zamknięta i chcesz debugować aplikację, możesz przejść do Edytuj schemat iw lewym menu wybierz Uruchom, a następnie podczas uruchamiania wybierz Czekaj na uruchomienie pliku wykonywalnego, a następnie uruchom aplikację, gdy kliknij powiadomienie push

Edytuj schemat> Uruchom> Poczekaj na uruchomienie pliku wykonywalnego

salmanki43
źródło