检测是否通过推送通知启动/打开了应用程序

joa*_*oao 162 push-notification apple-push-notifications uiapplicationdelegate ios ios6

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

我猜这个发布活动可以在这里找到:

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

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

    }
}
Run Code Online (Sandbox Code Playgroud)

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

sha*_*gao 183

请参阅此代码:

- (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
    }
}
Run Code Online (Sandbox Code Playgroud)

与...一样

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
Run Code Online (Sandbox Code Playgroud)

  • @ManuelM.这是一个很好的答案,它显示了如何检测后台中的应用程序何时从推送通知转到前台.对于应用程序未运行时,您需要M.Othman的答案如下. (19认同)
  • 像其他人指出的那样,这并没有检测到"从推送通知中启动/打开".收到通知时会调用此方法,而不是在打开通知时调用.因此,如果您在bg中收到通知但点击了应用图标以打开该应用,则此处的代码仍会运行,您可能会打开一个用户无意打开的页面. (16认同)
  • 我接到应用程序的调用:didReceiveRemoteNotification:点击通知后,无论应用程序是在后台还是根本没有运行,所以这个答案完全符合我的需求.在iOS 7和8上测试过 (6认同)
  • @ManuelM.如果选中后台模式 - 远程通知,此方法不会告知应用是否通过通知中心与应用图标打开.当它没有被检查时它会这样做.我已经记录了这篇文章的不同之处:http://stackoverflow.com/questions/32061897/ios-push-notification-how-to-detect-if-the-user-tapped-on-notification-when-the/ 32079458#32079458 (4认同)
  • 确认这适用于Google Cloud Messaging. (2认同)

M.O*_*man 126

迟到但也许有用

当应用程序未运行时

- (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");
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在上面的代码段中,通知不应声明为(UILocalNotification*)而是(NSDictionary*) (2认同)
  • 这样您就可以在未运行时查看应用程序是否有任何通知!问题是,如何检测应用程序是否是从通知中打开的。在这种情况下,即使应用程序根本没有运行,也会调用 didReceiveRemoteNotification。- 我喜欢你的回答,因为它在很多情况下都很重要,但不是问题的正确答案。 (2认同)
  • 虽然晚了,但仍然是一个救星,特别是当您使用 React-Native 框架并在应用程序启动时进行大量额外的版本检查、凭据和测量或连接套接字时。然后想象一下发送静默通知以删除其他一些已撤销的通知。Bum 您的应用程序已启动并连接到套接字。 (2认同)

Eri*_*ner 30

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

生命周期方法

我们对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
Run Code Online (Sandbox Code Playgroud)

问题

好的,现在我们需要:

  1. 确定用户是否通过推送打开应用程序
  2. 根据推送状态更新视图
  3. 清除状态,以便后续打开不会将用户返回到相同位置.

棘手的是,当应用程序实际变为活动状态时,必须更新视图,这在所有情况下都是相同的生命周期方法.

我们解决方案的草图

以下是我们解决方案的主要组成部分:

  1. notificationUserInfo在AppDelegate上存储实例变量.
  2. 设置notificationUserInfo = nilapplicationWillEnterForegrounddidFinishLaunchingWithOptions.
  3. 设置notificationUserInfo = userInfodidReceiveRemoteNotification:inactive
  4. applicationDidBecomeActive总是调用自定义方法openViewFromNotification并传递self.notificationUserInfo.如果self.notificationUserInfo为nil则提前返回,否则根据找到的通知状态打开视图self.notificationUserInfo.

说明

当从push打开didFinishLaunchingWithOptions或者applicationWillEnterForeground总是在之前调用时didReceiveRemoteNotification:inactive,所以我们首先在这些方法中重置notificationUserInfo,这样就没有陈旧状态了.然后,如果didReceiveRemoteNotification:inactive被调用,我们知道我们正在从推动开始,所以我们设置self.notificationUserInfo然后选择通过applicationDidBecomeActive将用户转发到右视图.

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

希望这可以帮助.

  • 除应用切换器外,还有两个边缘情况.1)当从顶部拉出通知中心并覆盖应用程序时2)当从底部拉出带有wifi/BT /等的iOS面板并覆盖应用程序时.在所有三种情况下,只调用`applicationWillResignActive`,然后调用`applicationDidBecomeActive`.所以在调用`applicationWillResignActive`之后,在调用`applicationDidEnterBackground`或`applicationDidBecomeActive`之前不要保存收到的通知. (2认同)

Mob*_*Vet 24

这是一个很好的帖子......但它仍然缺少问题的实际解决方案(如各种评论中所指出的).

最初的问题是关于检测应用程序何时从推送通知启动 /打开,例如用户点击通知.这些答案实际上都没有涵盖这个案例.

当通知到达时,可以在呼叫流程中看到原因, application:didReceiveRemoteNotification...

当接收到通知被调用时再通知是由用户点击.正因为如此,你只能看着UIApplicationState用户点击它就无法分辨.

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

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

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)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特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)
      }
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经在iOS 9+上对这两种情况(背景中的应用程序,应用程序没有运行)进行了测试,它就像一个魅力.0.1s也很保守,实际值是~0.002s所以0.01也很好.

  • 这是所有StackOverflow中唯一可行的解​​决方案.我唯一想补充的是,当你支持iOS 10及更高版本时,你可以简单地使用`UNNotificationCenter` API,特别是UNNotificationCenterDelegate方法.只有当用户实际点击了通知时,那些API调用func`userNotificationCenter(UNUserNotificationCenter,didReceive:UNNotificationResponse,withCompletionHandler:@escaping() - > Void)`方法. (4认同)

Vla*_*iak 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
}
Run Code Online (Sandbox Code Playgroud)

}


onm*_*133 19

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

public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
   if launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] != nil {
      print("from push")
    }
}
Run Code Online (Sandbox Code Playgroud)

当应用程序处于后台时,用户点击推送通知

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

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

根据您的应用程序,它也可以向您发送静音推送content-available内部aps,所以也要注意这一点:)请参阅/sf/answers/2364529331/

  • 只有答案不是一个肮脏的黑客和纠正.我缺少的是如果应用程序在后台并且用户手动打开它,如何检查?虽然仍然可以检查推冷启动和从后台推. (2认同)

Mad*_*dhu 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Kevin完全正确.这让你想知道为什么苹果似乎把实习生设计成处理通知的过程...... (4认同)
  • 请注意,如果在用户处于其他屏幕时发送通知(例如,如果他们下拉状态栏然后从您的应用收到通知),则"通知中打开的应用"将是误报. (3认同)

Lon*_*Guy 12

对于swift:

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 on background

    }

}
Run Code Online (Sandbox Code Playgroud)


Har*_*hat 6

如果您SceneDelegate的应用程序中有,那么当您的应用程序被终止/终止并且您通过点击通知打开应用程序时,您应该使用下面的代码来管理本地/远程通知

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    
    //Handle Notification Response
    guard let notifiResponse = connectionOptions.notificationResponse else { return }
    
    if notifiResponse.notification.request.trigger is UNTimeIntervalNotificationTrigger { //Local Notification
        Messaging.messaging().appDidReceiveMessage(notifiResponse.notification.request.content.userInfo)
        
        print("Receive Local Notifications")            
    }
    else if notifiResponse.notification.request.trigger is UNPushNotificationTrigger{ //Remote Notification
        
        print("Receive Remote Notifications")
    }                
}
Run Code Online (Sandbox Code Playgroud)

AppDelegate当您的应用程序处于后台/前台状态时,使用您来管理本地/远程通知。

extension AppDelegate : UNUserNotificationCenterDelegate {   

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
                   
        if response.notification.request.trigger is UNTimeIntervalNotificationTrigger{
             print("Receive Local Notifications")
        }
        else if response.notification.request.trigger is UNPushNotificationTrigger{
             print("Receive Remote Notifications")
        }
    
        let userInfo = response.notification.request.content.userInfo
        completionHandler()
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
       print("willPresent Notifications")

       if notification.request.trigger is UNTimeIntervalNotificationTrigger{
            print("Receive Local Notifications")
       }
       else {
            print("Receive Remote Notifications")
       }
       completionHandler([.banner, .list, .sound])
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是新的“选定”答案 - 当前的顶级答案已经过时并且不起作用! (2认同)

Wes*_*Wes 5

为 Xamarin 用户发布此内容。

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

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

如果是远程通知,那就是UIApplication.LaunchOptionsRemoteNotificationKey

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

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

我发现即使在 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;
}
Run Code Online (Sandbox Code Playgroud)