【问题标题】:Detect if the app was launched/opened from a push notification检测应用程序是否从推送通知启动/打开
【发布时间】:2013-04-29 21:49:00
【问题描述】:

是否可以通过推送通知知道应用程序是否已启动/打开?

我想启动事件可以在这里捕获:

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

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

    }
}

但是,当应用程序处于后台时,我如何检测它是从推送通知中打开的?

【问题讨论】:

  • 这是一篇古老但非常有用的帖子。不幸的是,最重要的答案并不能真正解决问题(如 cmets 所示)。请考虑将新答案标记为“已接受”,因为当前答案不完整。
  • 此问题的浏览量超过 100k,但所选答案不正确或完整。对于访问者,请考虑按活跃度而非投票数进行排序,以找到现代解决方案。

标签: ios push-notification ios6 apple-push-notifications uiapplicationdelegate


【解决方案1】:

2021,Swift 5,仅限本地通知

UNUserNotificationCenter.current().delegate = self

extension YourClass: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let notificationIdentifier = response.notification.request.identifier

        // If this is called, then your app was opened from a local notification with this identifier
    }
}


【讨论】:

    【解决方案2】:

    我们遇到的问题是在应用启动后正确更新视图。这里有一系列复杂的生命周期方法,令人困惑。

    生命周期方法

    我们对 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 CALLED 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. 在 AppDelegate 上存储一个 notificationUserInfo 实例变量。
    2. applicationWillEnterForegrounddidFinishLaunchingWithOptions 中设置notificationUserInfo = nil
    3. didReceiveRemoteNotification:inactive 中设置notificationUserInfo = userInfo
    4. applicationDidBecomeActive 始终调用自定义方法openViewFromNotification 并传递self.notificationUserInfo。如果self.notificationUserInfo 为nil 则提前返回,否则根据self.notificationUserInfo 中的通知状态打开视图。

    说明

    当从推送打开时,didFinishLaunchingWithOptionsapplicationWillEnterForeground 总是在 didReceiveRemoteNotification:inactive 之前立即调用,因此我们首先在这些方法中重置 notificationUserInfo,这样就不会出现陈旧状态。然后,如果didReceiveRemoteNotification:inactive 被调用,我们知道我们正在从推送中打开,所以我们设置self.notificationUserInfo 然后被applicationDidBecomeActive 拾取以将用户转发到正确的视图。

    最后一种情况是,如果用户在应用切换器中打开应用(即在应用处于前台时双击主页按钮),然后收到推送通知。在这种情况下,只调用了 didReceiveRemoteNotification:inactive,而 WillEnterForeground 和 didFinishLaunching 都没有被调用,所以你需要一些特殊的状态来处理这种情况。

    希望这会有所帮助。

    【讨论】:

    • 终于找到了可行的方法,谢谢!当应用程序状态为 active 或应用程序正在恢复时,我想创建一个标志“appResuming”并在receive 方法中打开屏幕。当应用程序仍处于非活动状态时,这可能会导致更改 VC 出现问题。你的解决方案看起来很棒,直到 Apple 再次改变生命周期。
    • iOS 9 怎么样,生命周期方法的调用方式和顺序是否相同?我已经没有 iOS 9 设备,所以我无法正确测试。
    • 除了应用切换器之外,还有另外两种边缘情况。 1)当通知中心从顶部拉出并覆盖应用程序时 2)当iOS的带有wifi / BT / etc的面板从底部拉出并覆盖应用程序时。在所有三种情况下,只调用applicationWillResignActive,然后调用applicationDidBecomeActive。因此,在调用 applicationWillResignActive 之后,在调用 applicationDidEnterBackgroundapplicationDidBecomeActive 之前不要保存收到的通知。
    • 感谢@shelll 添加这些案例。它总是变得更复杂!我不确定iOS9。我会说假设它们是相同的可能是安全的,但谁知道呢。
    • 请注意。我今天正在测试 iOS 11 Beta 9,发现如果您的应用程序在前台,锁定手机,然后从锁定屏幕中选择推送通知,它会在调用 applicationWillEnterForeground 之前调用 didReceiveRemoteNotification:background 而不是我们在 iOS 10 上看到的是它调用 applicationWillEnterForeground 然后 didReceiveRemoteNotification:inactive - 所以这是一个尚未涵盖的边缘情况。在我看来,这是 iOS 代码中的一个错误,但考虑到 iOS 11 发布的时间有多近,这是需要注意的。
    【解决方案3】:

    M.Othman 对 Swift 5 的回答。(虽然使用 NSLog 是 not recommended anymore

    在设置显示RootViewController 所需的所有内容后添加以下内容。在您的application(_:didReceiveRemoteNotification) 中,您应该添加可以区分首次启动和后台启动类型的逻辑。

    if let launchOptions = launchOptions, 
    let notification = launchOptions[UIApplicationLaunchOptionsKey.remoteNotification] 
    as? [AnyHashable : Any] {
        NSLog("app recieved notification from remote \(notification)")
        self.application(application, didReceiveRemoteNotification: notification)
    } else {
        NSLog("app did not recieve notification")
    }
    

    可以在以下位置找到解决此问题的其他一些 Swift 特定答案:How to handle launch options in Swift 3 when a notification is tapped? Getting syntax problems

    【讨论】:

      【解决方案4】:

      如果您运行的是 iOS 13 或更高版本,请在您的 SceneDelegate 中使用此代码:

      func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
          
          guard let notificationResponse = connectionOptions.notificationResponse else { return }
          
          let pushTitle = notificationResponse.notification.request.content.title
          let pushSubtitle = notificationResponse.notification.request.content.subtitle
          let pushBody = notificationResponse.notification.request.content.body
          
          // do your staff here
      }
      

      【讨论】:

      • 这是 2021 年的正确做法。如果使用 SceneDelegateAppDelegate 中的 launchOptionsnil
      【解决方案5】:

      快速:

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

      【讨论】:

        【解决方案6】:

        M.Othman 的回答对于不包含场景委托的应用是正确的 对于 Scene Delegate Apps 这在 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
                }
        

        【讨论】:

        • 完美答案!
        【解决方案7】:

        当应用终止,用户点击推送通知时

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

        当应用在后台,用户点击推送通知时

        如果用户从系统显示的警报中打开您的应用,系统可能会在您的应用即将进入前台时再次调用此方法,以便您更新用户界面并显示信息与通知有关。

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

        根据您的应用程序,它还可以通过content-availableaps 内向您发送静默推送,因此也要注意这一点:) 请参阅https://stackoverflow.com/a/33778990/1418457

        【讨论】:

        • 唯一的答案感觉不像是肮脏的黑客和正确的。我缺少的是如果应用程序在后台并且用户手动打开它,如何检查?同时仍然能够检查推送冷启动并从后台推送。
        • @JochenÖsterreicher 你好,我在这里总结一下,请查看medium.com/@onmyway133/…
        【解决方案8】:

        用于“未运行”状态的 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
        }
        

        【讨论】:

          【解决方案9】:

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

          【讨论】:

            【解决方案10】:

            迟到但可能有用

            当应用程序未运行时

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

            被称为..

            你需要在哪里检查推送通知

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

            【讨论】:

            • 请注意,在上面的 sn-p 中,notification 不应声明为 (UILocalNotification *) 而是 (NSDictionary *)
            • 这样您就可以查看应用程序在未运行时是否有任何通知!问题是,如何检测应用程序是否从通知中打开。在这种情况下,即使应用程序根本没有运行,也会调用 didReceiveRemoteNotification。 - 我喜欢你的回答,因为它对很多情况都很重要,但不是问题的正确答案。
            • 你的答案和this answer是不是在做同样的事情?
            【解决方案11】:

            我将从我为自己创建的状态图开始,以便更准确地对其进行可视化并考虑所有其他状态: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkU3iLdZnOZoevkMzOeng7gs31IFhD-L/pubhtml?gid=0&single=true

            使用此图表,我们可以了解实际需要什么才能开发出适用于几乎所有可能用例的强大通知处理系统。

            完整解决方案↓

            • didReceiveRemoteNotification 中存储 notification 有效负载
            • 清除applicationWillEnterForegrounddidFinishLaunchingWithOptions 中存储的通知
            • 要解决控制中心/通知中心被拉动的情况,您可以使用标志 willResignActiveCalled 并将其设置为 false, 在 applicationWillResignActive 方法中将此设置为 true
            • didReceiveRemoteNotification 方法中,仅当 willResignActiveCalled 为 false 时才保存通知(userInfo)。
            • applicationDidEnterBackgroundapplicationDidBecomeActive 方法中将 willResignActiveCalled 重置为 false

            注意:在 Eric 的答案中,cmets 建议了类似的答案,但是,状态表有助于找到所有可能的场景,就像我在我的应用中所做的那样。

            如果没有处理任何特定情况,请在下面找到完整的代码并在下面评论:

            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 感谢您的建议,更新了所有详细信息的答案
            【解决方案12】:

            只有一种可靠的方法,它只适用于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
                }
                ...
            }
            

            【讨论】:

              【解决方案13】:
              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)
                      }
                  }
              }
              

              【讨论】:

                【解决方案14】:

                为 Xamarin 用户发布此内容。

                检测应用是否通过推送通知启动的关键是AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options) 方法和传入的选项字典。

                如果是本地通知,选项字典中将包含此键:UIApplication.LaunchOptionsLocalNotificationKey

                如果是远程通知,则为UIApplication.LaunchOptionsRemoteNotificationKey

                当键为LaunchOptionsLocalNotificationKey 时,对象的类型为UILocalNotification。 然后,您可以查看通知并确定它是哪个特定通知。

                专业提示:UILocalNotification 中没有标识符,UNNotificationRequest 也一样。在包含 requestId 的 UserInfo 中放置一个字典键,以便在测试 UILocalNotification 时,您将有一个特定的 requestId 可用于某些逻辑。

                我发现,即使在 iOS 10+ 设备上,当使用 UNUserNotificationCenterAddNotificationRequestUNMutableNotificationContent 创建位置通知时,当应用程序未运行时(我杀死了它),并通过点击启动通知中心的通知,字典仍然包含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;
                }
                

                【讨论】:

                  【解决方案15】:

                  当应用在后台作为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 和然后在启动中选择等待启动可执行文件,然后在单击推送通知时启动应用程序

                  编辑方案 > 运行 > 等待可执行文件启动

                  【讨论】:

                    【解决方案16】:

                    对于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)")
                    
                        }
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      这个问题的问题在于“打开”应用程序的定义不明确。应用程序要么从非运行状态冷启动,要么从非活动状态重新激活(例如,从另一个应用程序切换回它)。这是我区分所有这些可能状态的解决方案:

                      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 的一个小包装。

                      【讨论】:

                        【解决方案18】:

                        这是一篇陈旧的帖子......但它仍然缺少解决问题的实际解决方案(正如各种 cmets 中所指出的那样)。

                        最初的问题是关于检测应用程序何时启动 / 从推送通知中打开例如用户点击 通知。没有一个答案实际上涵盖了这种情况。

                        原因可以在通知到达时的调用流程中看到,application:didReceiveRemoteNotification...

                        在收到通知时调用并且在用户点击通知时再次调用。因此,您无法仅通过查看UIApplicationState 来判断用户是否点击了它。

                        此外,您不再需要在application:didFinishLaunchingWithOptions... 中处理应用程序“冷启动”的情况,因为在 iOS 9+(可能是 8)启动后再次调用application:didReceiveRemoteNotification...

                        那么,如何判断用户点击是否启动了事件链?我的解决方案是标记应用程序开始退出后台或冷启动的时间,然后在application:didReceiveRemoteNotification... 中检查该时间。如果小于0.1s,那么你可以很确定点击触发了启动。

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

                        斯威夫特 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 也可以。

                        【讨论】:

                        • 这似乎是区分实际点击通知和在应用上打开状态栏的唯一有效解决方案。
                        • 这是所有 StackOverflow 中唯一可行的解​​决方案。我唯一想补充的是,当您支持 iOS 10 及更高版本时,您可以简单地使用 UNNotificationCenter API,特别是 UNNotificationCenterDelegate 方法。只有当用户实际点击通知时,这些 API 才会调用 func userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -&gt; Void) 方法。
                        • swift 3 怎么看?
                        • 当应用处于非活动状态(用户向下滑动通知中心或向上滑动控制中心)并收到通知时,该解决方案不起作用。当用户点击通知时,应用没有收到applicationWillEnterForeground 调用,因此解决方案无法检测到点击。
                        • @DenTelezhkin 即使使用UNNotificationCenter,您也无法检测应用程序是否从终止状态启动,除非您进行时间戳检查...
                        【解决方案19】:

                        如果有人想在 swift 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
                            }
                        }
                        

                        【讨论】:

                        • 但是当应用程序终止时如何通过点击推送通知来知道应用程序是否打开
                        • 当有人点击推送时,无论是否终止,应用都会打开。并且 .inactive 案例正在调用
                        • 我需要通过点击推送来检测应用程序是否已打开,并希望导航到我在 Instagram 上看到的相应内容
                        • 本地通知怎么样?
                        【解决方案20】:

                        在 SWIFT 中:

                        我正在运行推送通知(带有后台获取)。当我的应用在后台并收到推送通知时,我发现 appDelegate 中的 didReceiveRemoteNotification 会被调用两次;一次是在收到通知时,另一次是在用户点击通知警报时。

                        要检测是否点击了通知警报,只需检查 appDelegate 中的 didReceiveRemoteNotification 中的 applicationState 原始值是否 == 1。

                        func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject: AnyObject]) {
                            // If not from alert click applicationState(1)
                            if (application.applicationState.rawValue != 1) {
                                // Run your code here
                            }
                        }
                        

                        我希望这会有所帮助。

                        【讨论】:

                          【解决方案21】:
                               // 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 一样,它会以某种方式检测到它,如果应用程序甚至关闭,它会将您重定向到新帖子或图片或您的个人资料等。
                          【解决方案22】:

                          对于 Swift 用户:

                          如果您想在通过推送或类似方式打开时启动不同的页面,您需要在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
                          

                          【讨论】:

                          • Delegate 没有成员 navigationController
                          • 在 AppDelegate.h 文件中创建一个导航控制器。我正在使用它并且它有效!
                          【解决方案23】:

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

                          【讨论】:

                          • 请注意,如果通知是在用户处于不同屏幕时发送的(例如,如果他们拉下状态栏然后收到通知),“从通知打开应用程序”将是误报来自您的应用程序)。
                          • @Kevin 没错。这让你想知道为什么苹果似乎会安排一个实习生来设计处理通知的过程......
                          • 我们如何检测是否点击了在活动状态下收到的通知
                          【解决方案24】:

                          我还没有尝试过,但也许你可以给自己发个通知? http://nshipster.com/nsnotification-and-nsnotificationcenter/

                          【讨论】:

                            【解决方案25】:

                            你可以使用:

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

                            处理远程推送通知。

                            在这里查看documentation

                            【讨论】:

                              【解决方案26】:

                              直接来自

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

                              如果应用正在运行并收到远程通知,应用会调用此方法来处理通知。

                              您对此方法的实现应使用通知来采取适当的行动。

                              稍晚一点

                              如果推送通知到达时应用程序未运行,则该方法会启动应用程序并在启动选项字典中提供适当的信息。

                              应用程序不会调用此方法来处理该推送通知。

                              相反,您的实现

                              application:willFinishLaunchingWithOptions:
                              

                              application:didFinishLaunchingWithOptions:
                              

                              方法需要获取推送通知负载数据并做出适当响应。

                              【讨论】:

                                【解决方案27】:

                                查看此代码:

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

                                【讨论】:

                                • @ManuelM。这是一个很好的答案,因为它展示了如何检测何时将后台应用程序从推送通知带到前台。当应用程序未运行时,您需要 M.Othman 的回答如下。
                                • 我接到了对 application:didReceiveRemoteNotification: 的调用,无论应用程序只是在后台还是根本没有运行,所以这个答案非常适合我的需要。在 iOS 7 和 8 上测试
                                • 就像其他人指出的那样,这不会检测到“从推送通知启动/打开”。这在收到通知时调用,而不是在打开时调用。因此,如果您在 bg 中收到通知但点击应用图标打开应用,您在此处的代码仍将运行,您可能会打开用户不打算打开的页面。
                                • @ManuelM。如果后台模式 - 远程通知被选中,此方法不会告诉应用程序是否通过通知中心与应用程序图标打开。未选中时会这样做。我已经在这篇文章中记录了差异:stackoverflow.com/questions/32061897/…
                                • 确认这适用于 Google Cloud Messaging。
                                【解决方案28】:

                                是的,你可以在appDelegate中通过这个方法检测:

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

                                对于本地通知:

                                - (void)application:(UIApplication *)application
                                didReceiveLocalNotification:(UILocalNotification *)notification
                                {
                                         /* your Code*/
                                }
                                

                                【讨论】:

                                • 如果应用未运行,则不会调用此方法。这就是这里的要求
                                • 我的问题不是处理通知,而是知道当你点击横幅时它是否被打开(当应用程序在后台时)。
                                猜你喜欢
                                • 2015-11-17
                                • 2016-06-22
                                • 1970-01-01
                                • 2015-06-23
                                • 2016-03-24
                                • 1970-01-01
                                • 1970-01-01
                                • 2016-11-07
                                • 2014-06-06
                                相关资源
                                最近更新 更多