Визначте, чи додаток було запущено / відкрито з push-повідомлення


172

Чи можна дізнатися, чи додаток було запущено / відкрито з натискання сповіщення?

Я здогадуюсь, що стартову подію можна зловити тут:

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

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

    }
}

Однак як я можу виявити, що його відкрили з натискання сповіщення, коли додаток знаходився у фоновому режимі?


6
Це стара, але дуже корисна публікація. На жаль, головні відповіді фактично не вирішують проблему (як свідчать коментарі). Розгляньте позначення нової відповіді як "прийнято", оскільки поточна відповідь не завершена.
MobileVet

1
Це запитання має 100k + переглядів, але вибрана відповідь невірна або повна. Для відвідувачів розглядайте можливість сортування за допомогою 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

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

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. Оновіть подання залежно від стану натискання
  3. Очистіть стан, щоб наступні відкриття не повернули користувача до того ж положення.

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

Ескіз нашого рішення

Ось основні компоненти нашого рішення:

  1. Зберігайте notificationUserInfoзмінну екземпляра в AppDelegate.
  2. Встановити і notificationUserInfo = nilв applicationWillEnterForegroundі didFinishLaunchingWithOptions.
  3. встановити notificationUserInfo = userInfoвdidReceiveRemoteNotification:inactive
  4. З applicationDidBecomeActiveзавжди викликати призначений для користувача метод openViewFromNotificationі передати self.notificationUserInfo. Якщо self.notificationUserInfoнуль, тоді поверніться достроково, інакше відкрийте представлення на основі стану сповіщення, знайденого в self.notificationUserInfo.

Пояснення

Під час відкриття з натискання didFinishLaunchingWithOptionsабо applicationWillEnterForegroundзавжди викликається безпосередньо перед цим didReceiveRemoteNotification:inactive, тому ми спочатку скидаємо сповіщенняUserInfo цими методами, щоб не було старого стану. Тоді, якщо didReceiveRemoteNotification:inactiveдзвонимо, ми знаємо, що ми відкриваємося з поштовху, тому ми встановлюємо, self.notificationUserInfoякий потім підбираєтьсяapplicationDidBecomeActive щоб пересилати користувача до потрібного подання.

Є один остаточний випадок, якщо користувач відкриває додаток у перемикачі програм (тобто подвійним натисканням на кнопку додому, коли програма знаходиться на передньому плані), а потім отримує поштове повідомлення. У цьому випадку didReceiveRemoteNotification:inactiveвикликується лише виклик, і ні WillEnterForeground, ні didFinishLaunching не викликається, тому для обробки цього випадку вам потрібен спеціальний стан.

Сподіваюся, це допомагає.


Нарешті щось працює, дякую! Я хотів створити прапор "appResuming" і відкрити екран receiveметодами, коли стан додатка активний або додаток поновлюється. Це може призвести до проблем зі зміною ЖК, коли програма все ще неактивна. Ваше рішення виглядає чудово, поки Apple знову не змінить життєвий цикл.
shelll

Як щодо iOS 9, чи називаються методи життєвого циклу однаковим чином і порядком? У мене вже немає пристроїв iOS 9, тому я не можу перевірити це належним чином.
shelll

2
Є ще два краєвидні випадки, крім програми перемикача програм. 1) Коли центр сповіщень витягується зверху та накладає додаток 2) Коли панель iOS із wifi / BT / тощо витягується знизу та накладає додаток. У всіх трьох випадках просто applicationWillResignActiveвикликається і, а потім applicationDidBecomeActive. Тож після applicationWillResignActiveвиклику не зберігайте отримане повідомлення, поки не буде викликано applicationDidEnterBackgroundабо applicationDidBecomeActive.
shelll

Дякуємо, що додали ці випадки @shelll. Це завжди стає складніше! Я не впевнений у iOS9. Я б сказав, що, напевно, можна вважати, що вони однакові, але хто знає.
Ерік Коннер

Просто голови вгору. Сьогодні я тестував iOS 11 Beta 9 і виявив, що у випадку, коли у вас додаток на передньому плані, заблокуйте телефон, а потім виберіть натиснене повідомлення з блокованого екрана, воно викликає didReceiveRemoteNotification: фон перед викликом програмиWillEnterForeground, а не що ми бачимо на iOS 10, де він викликає applicationWillEnterForeground, а потім didReceiveRemoteNotification: неактивний - тому цей крайній випадок ще не висвітлений. На мою думку, це помилка в iOS-коді, але враховуючи, наскільки близький випуск iOS 11, варто пам’ятати про це.
Рой

24

Це добре зношений пост ... але все ще не вистачає фактичного рішення проблеми (на що вказується в різних коментарях).

Початкове запитання стосується виявлення, коли додаток було запущено / відкрито з натисненого повідомлення, наприклад, користувач натискає на сповіщення. Жодна з відповідей насправді не охоплює цю справу.

Причину можна побачити в потоці викликів, коли надходить повідомлення, application:didReceiveRemoteNotification...

отримує виклик, коли повідомлення отримано І знову, коли користувач натискає сповіщення. Через це ви не можете зрозуміти, просто подивившись на UIApplicationStateте, як користувач натиснув його.

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

Отже, як ви можете сказати, чи натиснути користувач запустив ланцюжок подій? Моє рішення - позначити час, коли програма починає виходити з фону або холодного початку, а потім перевірити цей час application:didReceiveRemoteNotification.... Якщо вона менше 0,1 с, то ви можете бути впевнені, що натискання спрацьовує при запуску.

Швидкий 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)
    }
  }
}

Швидкий 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,1s теж досить консервативний, фактичне значення ~ 0,002s, тому 0,01 також добре.


1
Це здається єдиним робочим рішенням, яке розрізняє фактичне натискання на сповіщення та відкриття рядка стану через додаток.
liviucmg

4
Це єдине робоче рішення з усіх StackOverflow. Єдине, що я хотів би додати, це коли ви підтримуєте iOS 10 та новіші версії, ви можете просто використовувати UNNotificationCenterAPI, зокрема методи UNNotificationCenterDelegate. userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) Метод функціонування виклику API лише тоді, коли користувач фактично натиснув повідомлення.
DenHeadless

як виглядає швидкий 3?
Jochen Österreicher

Рішення не працює, коли програма перебуває в неактивному стані (користувач провела вниз центр сповіщень або провела вгору центр управління) і отримує сповіщення. Коли користувач натискає на сповіщення, додаток не отримує 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


2
Відповідайте лише, що не відчуваєте себе брудною рушницею і правильною. Що мені не вистачає, якщо програма знаходиться у фоновому режимі, і користувач відкриває її вручну, як це перевірити? Поки ще можна перевірити на холодний старт і натиснути з фону.
Jochen Österreicher

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
@Kevin Рівно. Змушує задуматися, чому, здавалося б, 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

    }

}

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
Мені потрібно виявити, чи відкрито додаток, натиснувши кнопку натискання і хоче перейти до відповідного вмісту. Я бачив це в
instagram

Як щодо місцевих сповіщень?
Амір Шабані

3

Опублікував це для користувачів Xamarin.

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

У словнику параметрів цей ключ буде в ньому, якщо це місцеве сповіщення: UIApplication.LaunchOptionsLocalNotificationKey .

Якщо це віддалене повідомлення, воно буде UIApplication.LaunchOptionsRemoteNotificationKey .

Коли ключ LaunchOptionsLocalNotificationKey, об’єкт має типUILocalNotification . Потім можна переглянути сповіщення та визначити, яке саме воно є.

Підказка: в ньому UILocalNotificationнемає ідентифікатора, як і у випадку UNNotificationRequest. Помістіть у UserInfo ключ словника, що містить requestId, щоб під час тестуванняUILocalNotification вас буде доступний певний запитId, який може базуватися на деякій логіці.

Я виявив, що навіть на пристроях iOS 10+, коли під час створення сповіщень про місцеположення за допомогою UNUserNotificationCenter's AddNotificationRequest& UNMutableNotificationContent, що коли програма не працює (я її вбив), і запускається, торкнувшись сповіщення в центрі сповіщень, що словник все ще містить то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;
}

2

Прямо з документації на

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

Якщо програма запущена та отримує віддалене сповіщення, програма закликає цей метод для обробки сповіщення.

Ваша реалізація цього методу повинна використовувати сповіщення для прийняття відповідного курсу дій.

І трохи пізніше

Якщо додаток не працює, коли надходить push-сповіщення, метод запускає додаток та надає відповідну інформацію у словнику параметрів запуску.

Додаток не закликає цей метод для обробки цього push-повідомлення.

Натомість ваша реалізація

application:willFinishLaunchingWithOptions:

або

application:didFinishLaunchingWithOptions:

Метод повинен отримати дані про навантаження на push-повідомлення і відповісти відповідним чином.


2

Почну з державним графіком , який я створив для мого власного використання , щоб візуалізувати його більш точно і врахувати всі інші держави: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkU3iLdZnOZoevkMzOeng7gs31IFhD-L/pubhtml ? gid = 0 & одиночний = вірно

За допомогою цієї діаграми ми можемо побачити, що насправді потрібно для розробки надійної системи обробки повідомлень, яка працює майже у всіх можливих випадках використання.

Повне рішення ↓

  • Зберігати корисне навантаження повідомлень у didReceiveRemoteNotification
  • Очистити збережене сповіщення у applicationWillEnterForeground та didFinishLaunchingWithOptions
  • Для вирішення випадків, коли центр керування / Центр сповіщень витягнувся, ви можете використовувати прапор willResignActiveCalled та встановити його на початкове значення false , встановити це як true у методі applicationWillResignActive ,
  • У didReceiveRemoteNotification методі зберігайте сповіщення (userInfo) лише тоді, коли willResignActiveCalled буде помилковим.
  • Скиньте 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 Дякую за пропозицію, оновив відповідь з усіма подробицями
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)
        }
    }
}

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 повідомлень.

Перевірте тут документацію



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.


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

    }
}

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

0

Відповідь M.Othman є правильним для додатків , які не містять сцени делегата для сцени делегат додатки Це працює для мене на прошивці 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

У делегата немає учасника навігації
Контролер

1
Створіть навігаційний контролер у файлі AppDelegate.h. Я використовую його, і він працює!
AAA

-1

В SWIFT:

Я запускаю Push Notifications (із фоном отримання). Коли моя програма знаходиться у фоновому режимі, і я отримую поштовх, я виявив, що didReceiveRemoteNotification в appDelegate буде викликано двічі; один раз при отриманні сповіщення та іншому, коли користувач натискає сповіщення про сповіщення.

Щоб визначити, чи було натиснуто сповіщення про сповіщення, просто перевірте, чи не застосовано applicationState raw value == 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
    }
}

Я сподіваюся, що це допомагає.


-1

Коли додаток знаходиться у фоновому режимі як шанегао, ви можете використовувати його

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

Але якщо ви хочете запустити програму, і коли програма закрита, і ви хочете налагоджувати свою програму, ви можете перейти до редагування схеми та у лівому меню виберіть команду Запустити, а потім у запуску виберіть Зачекайте запуску виконуваного файлу, а потім запустіть програму, коли ви натисніть на push-повідомлення

Редагувати схему> Виконати> Зачекайте запуску виконуваного файлу

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.