【问题标题】:iOS push notification: how to detect if the user tapped on notification when the app is in background?iOS推送通知:如何在应用程序处于后台时检测用户是否点击了通知?
【发布时间】:2015-11-10 18:28:03
【问题描述】:

关于这个话题的stackoverflow线程很多,但我还是没有找到好的解决方案。

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

如果应用在后台,所有帖子都建议使用application:didReceiveRemoteNotification:并检查应用状态。但正如我所试验的那样(也正如这个 API 的名称所暗示的那样),这个方法在收到通知时被调用,而不是被点击。

所以问题是,如果应用程序已启动然后后台运行,并且您知道收到了来自application:didReceiveNotification 的通知(此时application:didFinishLaunchWithOptions: 不会触发),您如何知道用户是否恢复了应用程序通过点击通知或只是点击应用程序图标?因为如果用户点击了通知,则期望打开该通知中提到的页面。否则不应该。

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

谢谢。

编辑:

在阅读了下面的一个答案后,我想通过这种方式我可以澄清我的问题:

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

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

(B) 1.app进入后台; 2.收到通知; 3. 用户忽略通知,稍后点击应用图标; 4. 应用进入前台

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

或者,application:didReceiveRemoteNotification: 是否应该在步骤 3 中仅针对 (A) 触发,但我以某种方式配置了我的应用程序错误,所以我在步骤 2 中看到了它?

【问题讨论】:

  • 为您的有效负载使用自定义字典值并采取相应措施。很简单。
  • @soulshined 有效载荷中的一个字典可以表示用户是否点击了通知,对吗?例如您的朋友 A 发布了一篇文章 B,您可以在有效负载中说 {user: A, article: B},而应用程序在后台,您会收到 didReceiveRemoteNotification。如何知道应用何时恢复,是否应该显示文章?
  • @soulshined 我确实阅读了文档,并且确实了解了 didReceiveRemoteNotification 的作用。你真的读过我的问题吗?根据 Apple 的官方文档 didReceiveRemoteNotification “告诉代表正在运行的应用程序收到了远程通知”。我在问什么是判断用户是否点击通知的好方法。您提到的 SO 链接适用于当应用程序从新开始启动时,我问的是应用程序在后台时的场景。
  • @soulshined 好吧,也许我说得不够清楚。我的意思是如果应用程序完全退出,而不是在后台,是的 didFinishLaunching 将被调用。但是,如果您启动您的应用程序,然后将应用程序置于后台,现在一个通知进入,并且用户点击该通知,现在不会再次调用 didFinishLaunching。相反,将调用 applicationWillEnterForeground 和 applicationDidBecomeActive。您如何判断应用正在进入前台,因为用户点击了通知或应用图标?

标签: ios swift push-notification apple-push-notifications


【解决方案1】:

好吧,我终于想通了。

在目标设置 ➝ 功能选项卡 ➝ 后台模式中,如果您选中“远程通知”,application:didReceiveRemoteNotification: 将在通知到达后立即触发(只要应用程序在后台),在这种情况下无法判断用户是否会点击通知。

如果您取消选中该框,application:didReceiveRemoteNotification: 将仅在您点击通知时触发。

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

希望这对遇到此问题的其他人有所帮助。 Apple 也没有清楚地记录它here,所以我花了一段时间才弄清楚。

【讨论】:

  • 我已将后台模式完全设置为“关闭”,但当应用处于后台模式时收到通知时仍会收到通知。
  • 太棒了!这对我有帮助。可惜我需要关闭远程通知。我想在推送通知到达并检测到用户点击时预取数据。我找不到实现这一目标的方法。
  • 我将后台模式设置为“开启”模式并启用远程通知。仍然无法检测到通知事件。
  • 我有同样的问题后台模式设置为“开启”并启用了远程通知,但是当通知以后台模式到达应用程序时仍然没有收到通知
  • @Bao - 我认为这会导致 Appstore 被拒绝,因为这个选项基本上是用来在后台下载与通知相关的内容。因此,如果您不下载任何内容,Apple 可能会拒绝您的应用。 developer.apple.com/library/ios/documentation/iPhone/Conceptual/…
【解决方案2】:

我一直在寻找和你一样的东西,实际上找到了一个不需要勾选远程通知的解决方案。

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

-(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 Reference > UIApplication Class Reference > UIApplicationState

【讨论】:

  • 当应用在屏幕上时,由于用户使用通知中心(从顶部滑动)或控制中心(从底部滑动)而导致应用处于 UIApplicationStateInactive 的情况如何。显然,没有办法判断 APNS 是在 Inactive 状态下收到还是用户实际点击了它。
  • @KostiaKim 你是对的,但这是一个超级边缘情况......除此之外,这个答案在前台应用程序之间的分离方面是最有效的......用户点击......后台应用
  • 谢谢,这种方式揭开了一切的神秘面纱。请记住,为了在应用程序处于后台时调用委托方法,推送通知必须包含 content-available 键,但通知必须是无声的(即不包含声音或徽章),如 @ 987654322@.
  • @KostiaKim 我实际上能够通过在func applicationDidEnterBackground(_ application: UIApplication)func applicationDidBecomeActive(_ application: UIApplication) 中的 false 这允许我在应用由于控制中心或通知列表而处于非活动状态时显示应用内通知
  • 这让我大吃一惊,为什么 Apple 没有呈现不同的事件,或者只是在元数据中添加一个标志来指示用户实际与通知进行了交互。对于影响用户对应用程序行为期望的关键信息,必须根据有关应用程序状态的环境信息“推断”用户是否采取了行动是非常不合理的。也许唯一要做的就是创建一个自定义操作以使用该信息打开应用程序?
【解决方案3】:

根据 iOS / XCode: how to know that app has been launched with a click on notification or on springboard app icon? 您必须像这样检查 didReceiveLocalNotification 中的应用程序状态:

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

虽然它对我来说并不完全有意义,但它似乎有效。

【讨论】:

  • 在这种情况下是行不通的。我以前试过。当该复选框被选中时(有关详细信息,请参阅我接受的答案),当通知到达时,即使用户没有点击通知(通知刚刚到达)。
  • 我不同意。我在后台功能中检查了“远程通知”,它按照我的回答中描述的方式工作。我也检查了“后台获取”。也许这会导致变化?
  • 您能 +1 我的回答吗? ;-) 我仍然需要一些投票才能更多地参与 stackoverflow。非常感谢
  • 是的,这就是解决方案。 tnx.
  • 请注意,如果通知是在用户处于不同屏幕时发送的(例如,如果他们拉下状态栏,然后从您的应用收到通知),这将是误报。
【解决方案4】:

如果有人想要在 swift 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?
  • 在 didReceiveRemoteNotification 上
  • @Hamid sh,,,, 我收到了所有状态的推送通知,即当应用程序处于前台、后台、关闭(终止)..!但我的问题是当应用程序处于后台状态并关闭(终止)状态时如何增加徽章计数????请详细告诉我我是如何做到的......?我的应用程序徽章计数仅在应用程序处于前台状态时增加.....?如果可能的话,请简要告诉我......!
【解决方案5】:

我也遇到了这个问题,但在 iOS 11 上使用了新的 UserNotifications 框架。

对我来说是这样的:

  • 新品上线:application:didFinishLaunchingWithOptions:
  • 从终止状态接收:application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • 在前台接收: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • 后台接收:userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:

【讨论】:

  • 这是在 iOS 11 + 中实现的方式
  • 这太复杂了?‍♂️
【解决方案6】:

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

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

它帮助了我。请欣赏:)

【讨论】:

  • 我看到 Apple 添加了这个,但不知道如何以这种方式获取通知的自定义有效负载。
  • 当应用程序处于后台状态并变为活动状态以及应用程序从终止状态启动时调用此函数
  • @NikhilMuskur 仅在启用“远程通知”时是吗?
【解决方案7】:

PushNotificationManager 类中有两个 Funcs 来处理收到的 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()
}

我还测试了远程通知的开启和关闭状态(在后台模式下)

【讨论】:

    【解决方案8】:

    对于 iOS 10 及更高版本,请将其放在 AppDelegate 中,以了解是否点击了通知(即使应用程序关闭或打开也可以使用)

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

    【讨论】:

    【解决方案9】:

    在我的情况下,后台模式关闭没有任何区别。但是,当应用程序被暂停并且用户点击通知时,我可以在这个回调方法中处理这种情况:

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

    【讨论】:

    • 这是苹果官方所说的方式。根据 Apple 的文档,每当用户与推送通知 UI 交互时都会调用它。如果应用不在后台,它将以后台模式启动应用并调用此方法。
    【解决方案10】:

    SWIFT 5.1

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

    我已经创建了

    public static var isNotificationFromApp: Bool = false

    AppDelegate 中,我在我的起始viewController 中设置它true,然后在我的通知中storyboard/viewController我只是检查一下:)

    希望能派上用场

    【讨论】:

      【解决方案11】:

      经过大量试验和错误,通过 Deployent 目标更改解决。

      我在使用 iOS 部署目标 9.010.0,XCode 版本 11.6 (11E708) 时遇到了同样的问题。 - 当应用程序处于后台时,没有通过点击通知获得任何委托方法调用。

      解决此问题的方法是将 Target 更改为 iOS 11.0。我终于开始在UNUserNotificationCenter 委托didReceive: 实现中的冷启动背景 场景中接到通知点击的呼叫。

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

      我正在使用的有效负载如下。请注意,没有设置content-availablemutable-content

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

      【讨论】:

        【解决方案12】:

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

        首先,

        import UserNotifications
        

        其次,使AppDelegate符合协议UNUserNotificationCenterDelegate

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

        第三,将UNUserNotificationCenter对象的代理设置为AppDelegate里面application(_:willFinishLaunchingWithOptions:)

        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 框架的可用性。

        【讨论】:

        • 这在一次与单个通知交互时起作用,用户删除所有通知的情况如何,有没有办法找出这个?
        • 复制到 Yatin Arora / Aleks
        • @ErkkiNokso-Koivisto 我用了他们的答案,但你真的不能称它为完全相同的副本。这是一个更详细的答案。
        • @Claudiu 我没试过。您可以尝试解除所有并打印 actionIdentifier 吗?如果它没有调用打印,则意味着此方法不会在此操作上被调用。 :(
        【解决方案13】:

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

        从苹果的文档中,您应该使用此方法下载与推送通知相关的新内容。此外,要使其正常工作,您必须启用来自后台模式的远程通知,并且您的推送通知有效负载必须包含 content-available 键,其值设置为 1。有关更多信息,请参阅 Apple doc @ 的使用推送通知启动下载部分987654321@.

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

        希望对你有所帮助。

        【讨论】:

        • @bhusan 你读过我的问题的细节吗?我看到 didReceiveRemoteNotification 在通知刚到达时被调用(在用户点击它之前)。我想知道用户是否点击了它。
        • 您没有回答 OP 的问题。他不只是想知道有通知。他想知道用户是否通过点击远程通知打开了应用程序。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-04
        • 2023-03-11
        • 2018-07-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多