Push-уведомление iOS: как определить, нажал ли пользователь на уведомление, когда приложение находится в фоновом режиме?

116

По этой теме есть много потоков stackoverflow, но я все еще не нашел хорошего решения.

Если приложение не в фоновом режиме, можно проверить launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]в application:didFinishLaunchingWithOptions:вызове , чтобы увидеть , если он открыт с уведомлением.

Если приложение работает в фоновом режиме, во всех сообщениях предлагается использовать application:didReceiveRemoteNotification:и проверять состояние приложения. Но, как я экспериментировал (а также как следует из названия этого API), этот метод вызывается при получении уведомления, а не при нажатии.

Итак, проблема в том, что если приложение запущено, а затем находится в фоновом режиме, и вы знаете, что уведомление получено application:didReceiveNotification( application:didFinishLaunchWithOptions:на данном этапе не запускается), как узнать, возобновил ли пользователь приложение, нажав на уведомление или просто нажав на значок приложения? Потому что, если пользователь нажал на уведомление, ожидается, что откроется страница, упомянутая в этом уведомлении. Иначе не должно быть.

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

Спасибо.

РЕДАКТИРОВАТЬ:

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

Как мы можем различить эти 2 сценария:

(A) 1. приложение переходит в фоновый режим; 2. уведомление получено; 3. пользователь нажимает на уведомление; 4. Приложение выходит на передний план.

(B) 1. приложение переходит в фоновый режим; 2. уведомление получено; 3. пользователь игнорирует уведомление и позже нажимает на значок приложения; 4. Приложение выходит на передний план.

Поскольку application:didReceiveRemoteNotification:запускается в обоих случаях на шаге 2.

Или должен application:didReceiveRemoteNotification:срабатывать на шаге 3 только для (A), но я каким-то образом неправильно настроил свое приложение, поэтому я вижу его на шаге 2?

Бао Лэй
источник
Используйте настраиваемое значение словаря для полезной нагрузки и действуйте соответственно. Довольно просто.
soulshined
@soulshined словарь в полезной нагрузке может представлять, нажал ли пользователь на уведомление, верно? Например, ваш друг A опубликовал статью B, вы можете сказать {user: A, article: B} в полезной нагрузке, пока приложение работает в фоновом режиме, и вы получите didReceiveRemoteNotification. Как вы узнаете, когда приложение возобновит работу, следует ли отображать статью?
Бао Лэй
2
@soulshined Я прочитал документацию и узнал, что делает didReceiveRemoteNotification. Вы действительно прочитали мой вопрос? Согласно официальной документации Apple, didReceiveRemoteNotification «сообщает делегату, что запущенное приложение получило удаленное уведомление». Я спрашиваю, как лучше всего узнать, нажал ли пользователь на уведомление. Ссылка SO, о которой вы упомянули, предназначена, когда приложение запускается с нуля, я спрашиваю сценарий, когда приложение находится в фоновом режиме.
Бао Лэй
1
@soulshined ОК, может, я недостаточно ясно выразился. Я имею в виду, что если приложение полностью закрыто, а не в фоновом режиме, да, будет вызван didFinishLaunching. Но если вы запустите свое приложение, а затем фоновое приложение, и теперь приходит уведомление, и пользователь нажимает на уведомление, и теперь didFinishLaunching больше не будет вызываться. Вместо этого будут вызываться applicationWillEnterForeground и applicationDidBecomeActive. Как узнать, что приложение переходит на передний план, потому что пользователь нажал на уведомление или значок приложения?
Бао Лэй

Ответы:

102

ОК, я наконец-то разобрался.

В целевых настройках ➝ вкладка «Возможности» ➝ «Фоновые режимы», если вы отметите «Удаленные уведомления», они application:didReceiveRemoteNotification:будут запускаться, как только поступит уведомление (пока приложение находится в фоновом режиме), и в этом случае невозможно определить, пользователь нажмет на уведомление.

Если вы снимите этот флажок, application:didReceiveRemoteNotification:будет срабатывать только при нажатии на уведомление.

Немного странно, что установка этого флажка изменит поведение одного из методов делегата приложения. Было бы лучше, если бы этот флажок был установлен, Apple использует два разных метода делегирования для получения уведомлений и касания уведомлений. Я думаю, что большинство разработчиков всегда хотят знать, нажимается ли уведомление или нет.

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

введите описание изображения здесь

Бао Лэй
источник
6
У меня фоновые режимы полностью отключены, но я все равно получаю уведомления, когда приходит уведомление с приложением в фоновом режиме.
Готфрид
5
Большой! Это мне помогло. Однако жаль, что мне нужно отключить удаленные уведомления. Я хотел бы предварительно выбрать данные, когда приходит push-уведомление, И обнаруживать нажатие пользователя. Я не могу найти способ добиться этого.
Питер Феннема
1
Я установил фоновый режим в режим «ВКЛ» и включил удаленные уведомления. По-прежнему не удается обнаружить событие уведомления.
Калим Сайяд
1
У меня такая же проблема. Для фонового режима установлено значение «ON» и включено удаленное уведомление, но я все еще не получаю уведомление, когда уведомление приходит в приложение в фоновом режиме
Swapnil Dhotre
@Bao - я думаю, что это вызовет отказ в Appstore, поскольку этот параметр в основном используется для загрузки контента, связанного с уведомлением в фоновом режиме. Поэтому, если вы не загружаете какой-либо контент, Apple может отклонить ваше приложение. developer.apple.com/library/ios/documentation/iPhone/Conceptual/…
niks
69

Я искал то же самое, что и вы, и на самом деле нашел решение, которое не требует отмечать удаленное уведомление.

Чтобы проверить, нажал ли пользователь, приложение работает в фоновом режиме или активно, вам просто нужно проверить состояние приложения в

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{

    if(application.applicationState == UIApplicationStateActive) {

        //app is currently active, can update badges count here

    }else if(application.applicationState == UIApplicationStateBackground){

        //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here

    }else if(application.applicationState == UIApplicationStateInactive){

        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here

    }

Для получения дополнительной информации проверьте:

Справочник по UIKit Framework> Справочник по классам UIApplication> UIApplicationState

Dennism
источник
10
Как насчет того, чтобы приложение стало UIApplicationStateInactive в результате взаимодействия пользователя с центром уведомлений (проведите пальцем сверху) или центром управления (проведите пальцем снизу), когда приложение находится на экране. По-видимому, невозможно определить, был ли APNS получен в неактивном состоянии или пользователь действительно перешел на него.
Костя Ким
1
@KostiaKim Вы правы, но это супер крайний случай ... кроме этого, этот ответ является наиболее верным с точки зрения разделения между приложениями на переднем плане ... пользователь нажимает ... приложение в фоновом режиме
Дорогая,
Спасибо, это все демистифицирует. Помните, что для вызова метода делегата, когда приложение находится в фоновом режиме, push-уведомление должно включатьcontent-available ключ, но тогда уведомление должно быть беззвучным (то есть не включать звук или значок), как указано в официальных документах. ,
Nickkk 06
1
@KostiaKim Я действительно смог решить проблему незнания, был ли открыт центр управления или действительно ли пользователь нажал на уведомление, добавив логическое значение, для которого установлено значение true в func applicationDidEnterBackground(_ application: UIApplication)и false в этом, func applicationDidBecomeActive(_ application: UIApplication)это позволило мне показать в приложении уведомления, когда приложение неактивно из-за центра управления или списка уведомлений
DatForis
3
Мне непонятно, почему Apple не представила другое событие или просто не добавила флаг в метаданные, чтобы указать, что пользователь действительно взаимодействовал с уведомлением. Необходимость «выводить», предпринял ли пользователь какое-либо действие, основываясь на внешней информации о состоянии приложения, довольно неразумна для ключевой информации, которая влияет на ожидания пользователя от поведения приложения. Возможно, единственное, что нужно сделать, - это создать настраиваемое действие, чтобы открыть приложение с этой информацией?
Allan Nienhuis
20

Согласно iOS / XCode: как узнать, что приложение было запущено щелчком по уведомлению или по значку приложения-трамплина? вы должны проверить состояние приложения в didReceiveLocalNotification следующим образом:

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

Хотя для меня это не совсем понятно, похоже, это работает.

Вернер Кратохвиль
источник
1
В этом сценарии это не сработает. Я пробовал это раньше. Когда этот флажок установлен (подробности см. В моем принятом ответе), когда приходит уведомление, будет введена ваша строка с комментарием «// пользователь нажал уведомление», даже если пользователь не нажал на уведомление (уведомление только что пришло. ).
Бао Лэй
3
Я не согласен. В моих фоновых возможностях установлен флажок «Удаленные уведомления», и он работает так, как описано в моем ответе. Я также проверил "Фоновую выборку". Может быть, это вызывает изменение?
Вернер Кратохвиль
не могли бы вы +1 мой ответ? ;-) Мне все еще нужно несколько голосов, чтобы больше участвовать в stackoverflow. Большое спасибо
Вернер Кратохвиль
Yupe, это решение. Тпй.
Афшин
Обратите внимание, что это будет ложным срабатыванием, если уведомление будет отправлено, когда пользователь находится на другом экране (например, если он потянет вниз строку состояния, а затем получит уведомление от вашего приложения).
Кевин Купер
16

Если кому-то это нужно в быстрой версии 3.0

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
    }

для swift 4

switch UIApplication.shared.applicationState {
case .active:
    //app is currently active, can update badges count here
    break
case .inactive:
    //app is transitioning from background to foreground (user taps notification), do what you need when user taps here
    break
case .background:
    //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
    break
default:
    break
}
Хамид Шахсавари
источник
В какой триггер мы должны добавить этот код, didReceiveRemoteNotification или didFinishLaunchingWithOptions?
Dashrath
2
On didReceiveRemoteNotification
Хамид Шахсавари
@Hamid sh ,,,, Я получил push-уведомление во всех состояниях, то есть когда приложение находится в открытом состоянии, в фоновом режиме, закрыто (завершено) ..! но моя проблема в том, что как увеличить количество значков, когда приложение находится в фоновом состоянии и закрыть (завершить) состояние ???? Расскажите, пожалуйста, подробнее, как я это делаю ....? количество значков моего приложения увеличивается только тогда, когда приложение находится в рабочем состоянии .....? если можно, расскажите вкратце .......!
Kiran jadhav
9

Я тоже столкнулся с этой проблемой - но на iOS 11 с новым UserNotificationsFramework.

Вот для меня это так:

  • Новый запуск: application:didFinishLaunchingWithOptions:
  • Получено из конечного состояния: application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • Получено на переднем плане: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • Получено в фоновом режиме: userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
до
источник
Это способ сделать это в iOS
11+
Это просто так сложно 🤦‍♂️
Зорайр
8

Если у вас установлен флажок «Фоновые режимы»> «Удаленные уведомления» == ДА, нажмите на событие уведомления, которое появится в:

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

Мне это помогло. Пожалуйста, наслаждайтесь :)

Алекс Осипов
источник
Я видел, что Apple добавила это, но не мог понять, как таким образом получить пользовательскую полезную нагрузку уведомления.
sudo
Эта функция вызывается, когда приложение находится в фоновом состоянии и становится активным, а также когда приложение запускается из убитого состояния
Нихил Мускур,
@NikhilMuskur только если включены "удаленные уведомления" а?
Зорайр,
@Zorayr Ага, верно
Нихил Мускур
4

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

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

}
DoruChidean
источник
Это официальный способ, который утверждает Apple. Согласно документу Apple, он будет вызываться всякий раз, когда пользователь взаимодействует с пользовательским интерфейсом push-уведомлений. Если приложение не в фоновом режиме, оно запустит приложение в фоновом режиме и вызовет этот метод.
Райан
3

Для iOS 10 и более поздних версий поместите это в AppDelegate, чтобы узнать, что уведомление нажито (работает, даже если приложение закрыто или открыто)

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
print("notification tapped here")
}
Ятин Арора
источник
1
Это правильный ответ для iOS 10+. Полное объяснение приведено в другом потоке здесь: stackoverflow.com/a/41783666/1761357 .
dead_can_dance
2

Внутри PushNotificationManagerкласса есть две функции для обработки полученного PushNotification :

class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate{

}

Поскольку я тестировал первый триггер, как только пришло уведомление

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

    completionHandler(UNNotificationPresentationOptions.alert)

    //OnReceive Notification
    let userInfo = notification.request.content.userInfo
    for key in userInfo.keys {
         Constants.setPrint("\(key): \(userInfo[key])")
    }

    completionHandler([])

}

И второй при нажатии на уведомление:

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

    //OnTap Notification
    let userInfo = response.notification.request.content.userInfo
    for key in userInfo.keys {
        Constants.setPrint("\(key): \(userInfo[key])")
    }

    completionHandler()
}

Я также тестировал его как при включенном, так и при выключенном состоянии удаленного уведомления (в фоновых режимах).

Сейед
источник
1

SWIFT 5.1

UIApplication.State у меня не сработало, потому что как только я прочитал отпечаток пальца (отображается модальный) в своем приложении, уведомление также выдается на верхней панели, и пользователь должен щелкнуть его.

Я создал

public static var isNotificationFromApp: Bool = false

in, AppDelegateи я установил его trueв своем старте, viewControllerа затем в своем уведомлении storyboard/ viewControllerя просто проверяю это :)

Надеюсь, это может пригодиться

Hourglasser
источник
-7

Вы можете настроить полезную нагрузку push-уведомления для вызова делегата приложения application:didReceiveRemoteNotification:fetchCompletionHandler: метода когда приложение находится в фоновом режиме. Вы можете установить здесь какой-либо флаг, чтобы при следующем запуске вашего приложения пользователем вы могли выполнить свою операцию.

Из документации Apple вы должны использовать эти методы для загрузки нового контента, связанного с push-уведомлением. Кроме того, для этого на работу, вы должны включить удаленное уведомление от режимов фонового режима , и полезной нагрузку уведомления толчка должен содержать content-availableключ с его значением , установленным на 1. Из дополнительной информации см Использования Push Notifications для того чтобы начать загрузку раздел из яблочного дока здесь .

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

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

Амит Дхас
источник
@bhusan, вы читали подробности моего вопроса? Я вижу, что didReceiveRemoteNotification вызывается, когда уведомление только что приходит (до того, как пользователь нажмет на него). Я хотел узнать, нажимал ли на нее пользователь.
Бао Лэй
Вы не ответили на вопрос OP. Он не просто хочет знать, что было уведомление. Он хочет знать, открыл ли пользователь приложение, нажав на удаленное уведомление.
Joe C