Определить, было ли приложение запущено / открыто из push-уведомления

172

Можно ли узнать, было ли приложение запущено / открыто из push-уведомления?

Я предполагаю, что событие запуска может быть поймано здесь:

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

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

    }
}

Тем не менее, как я могу обнаружить, что он был открыт из push-уведомления, когда приложение было в фоновом режиме?

Joao
источник
6
Это старый, но очень полезный пост. К сожалению, топовые ответы не решают проблему (как показывают комментарии). Пожалуйста, рассмотрите возможность пометить новый ответ как «принятый», так как текущий не завершен.
MobileVet
1
Этот вопрос имеет более 100 000 просмотров, но выбранный ответ неверный или полный. Для посетителей рассмотрите возможность сортировки по Active вместо Votes, чтобы найти современные решения.
Альберт Реншоу

Ответы:

187

Смотрите этот код:

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

такой же как

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
shanegao
источник
19
@ManuelM. Это хороший ответ, поскольку он показывает, как определить, когда приложение в фоновом режиме выводится на передний план из push-уведомления. Если приложение не запущено, вам нужен ответ М.Отмана ниже.
OpenUserX03
6
Я получаю призыв к приложению: didReceiveRemoteNotification: после нажатия на уведомление независимо от того, находится ли приложение в фоновом режиме или не работает вообще, поэтому этот ответ полностью соответствует моим потребностям. Протестировано на iOS 7 и 8
Newtz
16
Как отмечали некоторые другие, он не обнаруживает «запуск / открытие из push-уведомления». Это вызывается, когда уведомление получено, а не когда оно открыто. Поэтому, если вы получили уведомление в bg, но нажали значок приложения, чтобы открыть приложение, код, который вы здесь, все еще будет запускаться, и вы можете открыть страницу, которую пользователь не собирался открывать.
Бао Лей
4
@ManuelM. этот метод не сообщает, было ли приложение открыто через центр уведомлений или значок приложения, если выбран фоновый режим - удаленное уведомление. Это происходит, когда это не проверено. Я задокументировал разницу в этом посте: stackoverflow.com/questions/32061897/…
Бао Лей
2
Подтвердили, что это работает с Google Cloud Messaging.
CularBytes
127

поздно, но может быть полезно

Когда приложение не запущено

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

называется ..

где нужно проверить push-уведомление

NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
    NSLog(@"app recieved notification from remote%@",notification);
    [self application:application didReceiveRemoteNotification:notification];
} else {
    NSLog(@"app did not recieve notification");
}
M.Othman
источник
2
Обратите внимание, что в приведенном выше фрагменте уведомление должно быть объявлено не как (UILocalNotification *), а как (NSDictionary *)
cosmix
1
Таким образом, вы можете увидеть, были ли какие-либо уведомления для приложения, пока он не запущен! Вопрос был в том, как определить, было ли приложение открыто из уведомления. В этом случае вызывается didReceiveRemoteNotification, даже если приложение не было запущено вообще. - Мне нравится ваш ответ, потому что он очень важен для многих случаев, но не правильный ответ на вопрос.
Аксель Зехден
Ваш ответ и этот ответ делают одно и то же?
Мед
38

У нас была проблема с корректным обновлением представления после запуска приложения. Здесь есть сложные последовательности методов жизненного цикла, которые сбивают с толку.

Методы жизненного цикла

Наше тестирование для iOS 10 выявило следующие последовательности методов жизненного цикла для различных случаев:

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

Эта проблема

Хорошо, теперь нам нужно:

  1. Определить, если пользователь открывает приложение с толчка
  2. Обновите представление на основе состояния push
  3. Очистите состояние, чтобы последующие открытия не возвращали пользователя в ту же позицию.

Сложность в том, что обновление представления должно происходить, когда приложение фактически становится активным, что является одним и тем же методом жизненного цикла во всех случаях.

Эскиз нашего решения

Вот основные компоненты нашего решения:

  1. Сохраните notificationUserInfoпеременную экземпляра в AppDelegate.
  2. Установите notificationUserInfo = nilв оба applicationWillEnterForegroundи didFinishLaunchingWithOptions.
  3. Установить notificationUserInfo = userInfoвdidReceiveRemoteNotification:inactive
  4. Из applicationDidBecomeActiveвсегда вызывайте пользовательский метод openViewFromNotificationи передавайте self.notificationUserInfo. Если self.notificationUserInfonil, тогда вернитесь раньше, иначе откройте представление на основе состояния уведомления, найденного в self.notificationUserInfo.

объяснение

При открытии из push didFinishLaunchingWithOptionsили applicationWillEnterForegroundвсегда вызывается непосредственно перед этим didReceiveRemoteNotification:inactive, поэтому мы сначала сбрасываем NotionUserInfo в этих методах, чтобы не было устаревшего состояния. Затем, если didReceiveRemoteNotification:inactiveвызывается, мы знаем, что мы открываем с толчка, поэтому мы устанавливаем, self.notificationUserInfoкоторый затем выбирается, applicationDidBecomeActiveчтобы направить пользователя к правильному представлению.

Существует один последний случай, когда пользователь открывает приложение в переключателе приложений (т. Е. Дважды нажав кнопку «Домой», когда приложение находится на переднем плане), а затем получает push-уведомление. В этом случае didReceiveRemoteNotification:inactiveвызывается только и не вызывается ни WillEnterForeground, ни didFinishLaunching, так что вам нужно какое-то специальное состояние для обработки этого случая.

Надеюсь это поможет.

Эрик Коннер
источник
Наконец то, что работает, спасибо! Я хотел создать флаг «appResuming» и открыть экран в receiveметодах, когда состояние приложения активно или приложение возобновляет работу. Это может привести к проблемам со сменой VC, когда приложение все еще неактивно. Ваше решение выглядит великолепно, пока Apple снова не изменит жизненный цикл.
shelll
Как насчет iOS 9, методы жизненного цикла вызываются одинаково и в порядке? У меня уже нет устройств iOS 9, поэтому я не могу проверить это должным образом.
Shelll
2
Есть еще два крайних случая, кроме переключателя приложений. 1) Когда центр уведомлений вытягивается сверху и перекрывает приложение. 2) Когда панель iOS с wifi / BT / etc вытягивается снизу и накладывается на приложение. Во всех трех случаях только и applicationWillResignActiveназывается applicationDidBecomeActive. Поэтому после applicationWillResignActiveвызова не сохраняйте полученное уведомление до вызова applicationDidEnterBackgroundили applicationDidBecomeActive.
shelll
Спасибо за добавление этих случаев @shelll. Это всегда становится сложнее! Я не уверен насчет iOS9. Я бы сказал, что можно предположить, что они одинаковы, но кто знает.
Эрик Коннер
Просто один на один. Сегодня я тестировал iOS 11 Beta 9 и обнаружил, что в случае, когда ваше приложение находится на переднем плане, заблокируйте телефон, а затем выберите push-уведомление на экране блокировки, он вызывает didReceiveRemoteNotification: background непосредственно перед вызовом applicationWillEnterForeground, а не то, что мы видим на iOS 10, где он вызывает applicationWillEnterForeground, а затем didReceiveRemoteNotification: неактивно - так что этот крайний случай еще не рассмотрен. На мой взгляд, это ошибка в коде iOS, но, учитывая, насколько близок релиз iOS 11, об этом нужно знать.
Рой
24

Это хорошо изношенный пост ... но в нем все еще отсутствует реальное решение проблемы (как указано в различных комментариях).

Первоначальный вопрос касается определения, когда приложение было запущено / открыто из push-уведомления, например, пользователь нажимает на уведомление. Ни один из ответов на самом деле не охватывает этот случай.

Причину можно увидеть в потоке вызовов, когда приходит уведомление, application:didReceiveRemoteNotification...

вызывается при получении уведомления И снова, когда уведомление прослушивается пользователем. Из-за этого вы не можете сказать, просто взглянув на UIApplicationStateто, нажал ли пользователь.

Кроме того, вам больше не нужно справляться с ситуацией «холодного старта» приложения в том application:didFinishLaunchingWithOptions...виде, в каком он application:didReceiveRemoteNotification...вызывается снова после запуска в iOS 9+ (возможно, и в 8).

Итак, как вы можете определить, что пользователь нажал на цепочку событий? Мое решение состоит в том, чтобы отметить время, когда приложение начинает выходить из фона или холодного запуска, а затем отметить это время application:didReceiveRemoteNotification.... Если оно меньше 0,1 с, то вы можете быть уверены, что нажатие вызвало запуск.

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

Я проверил это для обоих случаев (приложение в фоновом режиме, приложение не запущено) на iOS 9+, и оно работает как шарм. 0,1 с тоже довольно консервативно, фактическое значение ~ 0,002 с, поэтому 0,01 тоже хорошо.

MobileVet
источник
1
Похоже, что это единственное рабочее решение, которое различает фактическое нажатие на уведомление и отображение строки состояния поверх приложения.
liviucmg
4
Это единственное рабочее решение из всех StackOverflow. Единственное, что я хотел бы добавить, - когда вы поддерживаете iOS 10 и выше, вы можете просто использовать UNNotificationCenterAPI, в частности методы UNNotificationCenterDelegate. Эти API вызывают userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) метод func только тогда, когда пользователь фактически нажал на уведомление.
DenHeadless
как выглядит swift 3?
Йохен Остеррайхер
Решение не работает, когда приложение находится в неактивном состоянии (пользователь перелистывает центр уведомлений или центр управления) и получает уведомление. Когда пользователь нажимает на уведомление, приложение не получает applicationWillEnterForeground вызов, в результате решение не может обнаружить касание.
DevGansta
@DevGansta Когда вы добавите свой класс, как UNUserNotificationCenter.current().delegateв application:didFinishLaunchingWithOptions, приложение будет звонить userNotificationCenter(didReceive response)после нажатия в случае, который вы описали
Дориан Рой
22

Когда приложение закрывается, и пользователь нажимает на push-уведомление

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

Когда приложение находится в фоновом режиме, и пользователь нажимает на push-уведомления

Если пользователь открывает ваше приложение из системного предупреждения, система может снова вызвать этот метод, когда ваше приложение собирается выйти на передний план, чтобы вы могли обновить свой пользовательский интерфейс и отобразить информацию, относящуюся к уведомлению.

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

В зависимости от вашего приложения, оно также может отправлять вам тихий толчок content-availableвнутри aps, так что имейте это в виду :) См. Https://stackoverflow.com/a/33778990/1418457

onmyway133
источник
2
Единственный ответ, который не чувствуется как грязный и правильный. Чего мне не хватает, если приложение работает в фоновом режиме и пользователь открывает его вручную, как это проверить? Пока еще можно проверить толчок холодного старта и оттолкнуться от фона.
Йохен Остеррайхер
1
@ JochenÖsterreicher Привет, я сводном здесь, пожалуйста , проверьте medium.com/@onmyway133/...
onmyway133
19

Swift 2.0 для состояния «не работает» (локальное и удаленное уведомление)

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

В application:didReceiveRemoteNotification:проверке того , как вы получили уведомление , когда приложение находится на переднем плане или в виде фона.

Если оно было получено в фоновом режиме, запустите приложение из уведомления.

-(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
Обратите внимание, что «Приложение, открытое из уведомлений» будет ложно положительным, если уведомление отправляется, когда пользователь находится на другом экране (например, если они опускают строку состояния и затем получают уведомление из вашего приложения).
Кевин Купер
4
@ Кевин Точно. Это заставляет задуматься, почему Apple, казалось бы, пригласила стажера разрабатывать процесс обработки уведомлений ...
Андреас
как мы можем обнаружить, если мы нажимаем на уведомление, полученное в активном состоянии
Mayank Jain
13

Для быстрой:

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
источник
4

Да, вы можете определить этот метод в appDelegate :

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

Для местного уведомления:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}
Невозможно
источник
1
Этот метод не вызывается, если приложение не запущено. Вот что спросили здесь
Pfitz
Моя проблема не в том, чтобы обработать уведомление, а в том, чтобы знать, было ли оно открыто при нажатии на баннер (когда приложение находится в фоновом режиме).
Жоао
3

если кто-то хочет получить ответ в Свифт 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
    }
}
Хамид Шахсавари
источник
но как узнать, открывается ли приложение, нажав push-уведомление, когда приложение завершается
user3804063
1
когда кто-то нажимает на приложение, приложение будет открыто, независимо от того, было оно завершено или нет. и неактивное дело зовет
Хамид Шахсавари
1
Мне нужно определить, если приложение открыто,
user3804063
Как насчет локальных уведомлений?
Амир Шабани
3

Размещать это для пользователей Xamarin.

Ключом к определению того, было ли приложение запущено с помощью push-уведомления, является AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)метод и словарь опций, которые передаются.

Словарь вариантов будет иметь этот ключ в нем , если это локальное оповещение: UIApplication.LaunchOptionsLocalNotificationKey.

Если это удаленное уведомление, оно будет UIApplication.LaunchOptionsRemoteNotificationKey.

Когда ключ LaunchOptionsLocalNotificationKey, объект имеет тип UILocalNotification. Затем вы можете посмотреть на уведомление и определить, какое именно это уведомление.

Pro-tip: в нем UILocalNotificationнет идентификатора, как и в случае с идентификатором UNNotificationRequest. Поместите ключ словаря в UserInfo, содержащий requestId, чтобы при тестировании у UILocalNotificationвас был конкретный requestId, на котором можно основывать некоторую логику.

Я обнаружил, что даже на устройствах с iOS 10+, когда при создании уведомлений о местоположении с помощью UNUserNotificationCenter« AddNotificationRequestUNMutableNotificationContent, когда приложение не запущено (я его убил) и запускается нажатием на уведомление в центре уведомлений, словарь по-прежнему содержит UILocalNotificaitonобъект.

Это означает, что мой код, который проверяет запуск на основе уведомлений, будет работать на устройствах iOS8 и 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
источник
2

Прямо из документации по

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

Если приложение работает и получает удаленное уведомление, приложение вызывает этот метод для обработки уведомления.

Ваша реализация этого метода должна использовать уведомление, чтобы принять надлежащий курс действий.

И чуть позже

Если приложение не запускается при получении push-уведомления, метод запускает приложение и предоставляет соответствующую информацию в словаре параметров запуска.

Приложение не вызывает этот метод для обработки этого push-уведомления.

Вместо этого ваша реализация

application:willFinishLaunchingWithOptions:

или

application:didFinishLaunchingWithOptions:

Метод должен получить данные полезных данных push-уведомлений и ответить соответствующим образом.

Pfitz
источник
2

Я начну с диаграммы состояний, которую я создал для своего собственного использования, чтобы более точно ее визуализировать и рассмотреть все другие состояния: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkUIFTHLGWLGWGW1W1W7 ? GID = 0 & = одна истинная

Используя эту таблицу, мы можем увидеть, что на самом деле требуется для разработки надежной системы обработки уведомлений, которая работает практически во всех возможных случаях использования.

Комплексное решение ↓

  • Хранить уведомление полезной нагрузки в didReceiveRemoteNotification
  • Очистить сохраненные уведомления в applicationWillEnterForeground и didFinishLaunchingWithOptions
  • Чтобы справиться со случаями, когда управляющий центр / центр уведомлений извлекается, вы можете использовать флаг willResignActiveCalled и изначально установить для него значение false , установите для него значение true в методе applicationWillResignActive ,
  • В методе didReceiveRemoteNotification сохраняйте уведомления (userInfo) только тогда, когда willResignActiveCalled имеет значение false.
  • Сброс willResignActiveCalled в false в методе applicationDidEnterBackground и applicationDidBecomeActive .

Примечание. Аналогичный ответ предлагается в комментариях к ответу Эрика, однако лист состояния помогает найти все возможные сценарии, как я делал в своем приложении.

Пожалуйста, найдите полный код ниже и комментарий ниже, если какой-либо конкретный случай не обрабатывается:

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 : здесь вы можете написать весь свой код для навигации по различным частям приложения, обработки баз данных (CoreData / Realm) и выполнения всех других действий, которые необходимо выполнить при получении уведомления.

   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
    }
  }
}
Четан Ананд
источник
лучше поставить это как комментарий, так как это не ответ.
Мэдди
@Maddy Спасибо за предложение, обновил ответ со всеми подробностями
Четан и
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
источник
1

Существует только один надежный способ, и он работает только для iOS 10+ :

Используя метод UNUserNotificationCenterреализации 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

Ты можешь использовать:

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

обрабатывать удаленные push-уведомления.

Проверьте здесь документацию

sunkehappy
источник
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")
            }
    }
Шон Дев
источник
Но что, если приложение было закрыто (прекращено). Как Twitter или Instagram, он каким-то образом обнаруживает это, и если приложение даже закрывается, оно перенаправляет вас на новую запись, фотографии или ваш профиль и т. Д.
Tarvo Mäesepp
0

Проблема с этим вопросом заключается в том, что «открытие» приложения не является четко определенным. Приложение либо запускается в холодном режиме из неработающего состояния, либо повторно активируется из неактивного состояния (например, из-за переключения обратно в другое приложение). Вот мое решение, чтобы различать все эти возможные состояния:

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

И MXDefaultsэто просто небольшая обертка для NSUserDefaults.

skensell
источник
0

Для 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
источник
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

Для iOS 10+ вы можете использовать этот метод, чтобы узнать, когда нажимается ваше уведомление, независимо от состояния приложения.

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

    //Notification clicked
    completionHandler()
}
manishsharma93
источник
0

Ответ М.Отмана верен для приложений, которые не содержат делегата сцены. Для приложений делегирования сцены. Это сработало для меня на iOS 13.

Вот код, который должен быть написан в сцене подключения

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

Код для делегата приложения для поддержки более ранних версий didFinishLaunchingWithOptions

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

            //Launched from push notification
        } else {

            //Launch from other source
        }
Номан Харун
источник
-1

Для пользователей Swift:

Если вы хотите открыть другую страницу при открытии из push или что-то в этом роде, вам нужно проверить это 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
источник
Делегат не имеет члена навигацииКонтроллер
Пабло Чегарра
1
Создайте контроллер навигации в файле AppDelegate.h. Я использую это, и это работает!
ААА
-1

В SWIFT:

Я запускаю Push-уведомления (с фоновой загрузкой). Когда мое приложение работает в фоновом режиме и я получаю push-уведомление, я обнаружил, что didReceiveRemoteNotification в appDelegate будет вызываться дважды; один раз, когда уведомление получено, и другой, когда пользователь нажимает на уведомление уведомления.

Чтобы определить, была ли нажата кнопка уведомления об уведомлении, просто проверьте, является ли необработанное значение applicationState == 1 внутри didReceiveRemoteNotification в 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
    }
}

Надеюсь, это поможет.

Johnny5
источник
-1

Когда приложение в фоновом режиме, как 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
    }
}

Но если вы хотите запустить приложение и когда приложение закрыто, и вы хотите отладить свое приложение, вы можете перейти к Edit Scheme и в левом меню выбрать Run, а затем в запуске выбрать Wait for exetable для запуска, а затем вы запускаете приложение, когда вы нажмите на push-уведомление

Редактировать схему> Выполнить> Дождаться запуска исполняемого файла

salmancs43
источник