IOS 推送通知: 当应用程序在后台时,如何检测用户是否点击了通知?

关于这个主题有很多堆栈溢出线程,但是我仍然没有找到一个好的解决方案。

如果应用程序不在后台,我可以在 application:didFinishLaunchingWithOptions:呼叫中检查 launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey],看看它是否从通知中打开。

如果应用程序是在后台,所有的职位建议使用 application:didReceiveRemoteNotification:和检查应用程序的状态。但是正如我所做的实验(也正如这个 API 的名字所暗示的那样) ,这个方法在接收到通知时被调用,而不是被点击。

所以问题是,如果应用程序被启动,然后后台,你知道一个通知是从 application:didReceiveNotification接收(application:didFinishLaunchWithOptions:不会触发这一点) ,你怎么知道用户是通过点击通知或只是点击应用程序图标恢复应用程序?因为如果用户点击了通知,预期将打开通知中提到的页面。否则就不应该。

我可以使用 handleActionWithIdentifier进行自定义操作通知,但这只有在点击自定义操作按钮时才会触发,而不是在用户点击通知的主体时触发。

谢谢。

编辑:

在阅读了下面的一个答案后,我想这样我可以澄清我的问题:

我们如何区分这两种情况:

(A)1.app 进入后台; 2. 接收到通知; 3. 用户点击通知; 4.app 进入前台

(B)1.应用程序进入后台;。接获通知;。用户忽略通知,稍后点击应用程序图标;。应用程序进入前台

因为 application:didReceiveRemoteNotification:在两种情况下都是在第2步触发的。

或者,如果 application:didReceiveRemoteNotification:仅在步骤3中触发(A) ,但是我不知怎么地配置了我的应用程序,所以我在步骤2中看到了它?

110985 次浏览

您可以将推送通知的有效负载配置为在应用程序处于后台时调用应用程序委托的 application:didReceiveRemoteNotification:fetchCompletionHandler:方法。您可以在这里设置一些标志,以便当用户下次启动应用程序时,您可以执行操作。

在苹果的文档中,您应该使用这些方法来下载与推送通知相关的新内容。此外,为了实现这一点,您必须启用来自后台模式的远程通知,并且您的推送通知有效负载必须包含 content-available键,其值设置为1。更多信息请参见使用推送通知从 apple doc 给你开始下载部分。

另一种方法是在推送通知有效负载中使用徽章计数。因此,下次启动应用程序时,您可以检查应用程序徽章计数。如果它大于零,执行您的操作和零/清除徽章计数从服务器也。

希望这个能帮到你。

好吧,我终于明白了。

在目标设置功能选项卡背景模式中,如果你选择“远程通知”,通知一到就会触发 application:didReceiveRemoteNotification:(只要应用在后台) ,在这种情况下,没有办法判断用户是否会点击通知。

如果取消选中该复选框,则仅当您点击通知时才会触发 application:didReceiveRemoteNotification:

有点奇怪的是,选中这个复选框会改变一个应用程序委托方法的行为。如果勾选该选项会更好,Apple 使用两种不同的委托方法来接收通知和点击通知。我认为大多数开发人员总是想知道通知是否被点击。

希望这对其他遇到这个问题的人有所帮助。苹果也没有清楚地记录 给你,所以我花了一段时间才弄明白。

enter image description here

根据 IOS/XCode: 如何知道应用程序已经通过点击通知或跳板应用程序图标启动? 您必须像下面这样检查 did ReceiveLocalNotification 中的应用程序状态:

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

虽然这对我来说没有完全意义,但它似乎起作用了。

我一直在寻找与您相同的东西,并实际上找到了一个解决方案,不需要远程通知被勾选。

要检查用户是否已点击,或应用程序是在后台或活动,您只需检查应用程序的状态

-(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 框架引用 > UIApplication 类引用 > UIApplicationState

如果有人想要迅捷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
}

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

如果你选择了“后台模式”> “远程通知”= = 是,点击通知事件将到达:

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

它帮助了我。请欣赏:)

我也遇到过这个问题ーー但是在 iOS11上使用了新的 UserNotifications框架。

对我来说是这样的:

  • 新发布: application:didFinishLaunchingWithOptions:
  • 从终止状态接收: application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • 前景收到: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • 背景接收: userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:

在我的情况下,背景模式 OFF 没有任何区别。然而,当应用程序被暂停,用户点击通知,我可以处理这种情况下的回调方法:

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


}

对于 iOS10及以上版本,把这个放在应用程序代理中,以了解通知被点击(甚至应用程序被关闭或打开)

func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
print("notification tapped here")
}

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

我还测试了远程通知的 ON 和 OFF 状态(在后台模式下)

SWIFT 5.1

UIApplication.State对我来说不起作用,因为一旦我在我的应用程序中读取指纹(模态显示) ,通知也扔在上栏,用户必须点击它。

我创造了

public static var isNotificationFromApp: Bool = false

AppDelegate中,我在我的起始 viewController中设置为 true,然后在我的通知 storyboard/viewControllerI 中检查:)

希望能派上用场

通过改变部署目标解决了这个问题,经过了大量的尝试和错误。

我在 iOS 部署目标 9.010.0,XCode Version 11.6(11E708)上也遇到了同样的问题。- 当应用程序在 背景中时,没有从点击通知中获得任何委托方法调用。

解决这个问题的方法是将 Target 改为 IOS 11.0。最后,我开始在 UNUserNotificationCenter委托 didReceive:实现中从 冷启动背景场景接收通知调用。

此外,willReceive:现在还在 前景场景中获得调用(正如预期的那样)。

我使用的有效载荷如下。注意,没有 content-availablemutable-content集。

{
"aps": {
"sound": "default",
"badge": 1,
"alert": {
"body": "test",
"title": "test"
}
}
}

正如 Aleks所提到的,我们可以使用 userNotificationCenter(_:didReceive:withCompletionHandler:)(医生)方法来捕获用户对通知的响应。我们需要将 UNUserNotificationCenter的共享对象的委托设置为应用委托的 application(_:didFinishLaunchingWithOptions:)application(_:willFinishLaunchingWithOptions:)方法。基本上我是这么做的。

首先,

import UserNotifications

第二,使 AppDelegate符合协议 UNUserNotificationCenterDelegate

@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
// your app delegate code
}

第三,将 UNUserNotificationCenter对象的委托设置为 application(_:willFinishLaunchingWithOptions:)内部的 AppDelegate

func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}

第四,实现委托方法 userNotificationCenter(_:didReceive:withCompletionHandler:)

func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let application = UIApplication.shared
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
let userInfo = response.notification.request.content.userInfo
self.handlePushNotification(application, userInfo: userInfo)
}
}

最后,handlePushNotificationMethod是您的私有方法,您可以在其中实现处理通知的所有逻辑。

func handlePushNotification(userInfo: [AnyHashable : Any]) {
if let aps = userInfo["aps"] as? NSDictionary {
...
}
}

每当用户点击通知或者用户对通知执行某种操作时,就会调用此方法(userNotificationCenter(_:didReceive:withCompletionHandler:))。对我来说,我只想得到通知。因此,我将响应的 actionIdentifier 与缺省操作标识符进行了比较。

实现时请注意 UserNotifications框架的可用性。

有一个’骇客’的方式来知道是否收到通知,而应用程序正在运行(前台)或从用户点击。

TLDR 版本 :
当应用程序从后台引入时,它的生命周期将经历 applicationWillEnterForeground

实施方法:
首先要过滤的(如多次声明的那样)是应用程序是否处于非活动状态。

if application.applicationState == .inactive // You can ignore all the rest

背景状态将永远不会发生和活动,如果明显的前景。应用程序状态的其余问题是 真的非活动(用户拉动控制中心)或用户点击打开。
在这两种情况下,状态都是 inactive
然而 ,当用户点击通知时,应用程序首先通过 applicationWillEnterForeground。只需检查从 applicationWillEnterForegrounddidReceiveRemoteNotification的时间差就可以解决这个问题。

// In AppDelegate


var applicationWillEnterForegroundTime = Date()


func applicationWillEnterForeground(_ application: UIApplication) {
applicationWillEnterForegroundTime = Date()
}


func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    

if application.applicationState == .inactive,
Date().timeIntervalSince(applicationWillEnterForegroundTime) < 1.0 {
// Push notification was opened by user tap
}
}