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


171

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

我想启动事件可以在这里找到:

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

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

    }
}

但是,当应用程序在后台运行时,如何从推送通知中检测到它已打开?


6
这是一个古老但非常有用的文章。不幸的是,最重要的答案并不能真正解决问题(如注释所示)。由于当前答案尚未完成,请考虑将新答案标记为“已接受”。
MobileVet

1
该问题有超过10万的观看次数,但所选答案不正确或完整。对于访问者,考虑按活动而不是按投票进行排序,以找到现代解决方案。
艾伯特·伦肖

Answers:


187

请参阅以下代码:

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

19
@ManuelM。这是一个很好的答案,因为它显示了如何检测何时将推送通知中的后台应用程序带到前台。对于应用程序未运行时,您需要在下面提供M.Othman的答案。
OpenUserX03

6
我接到了application:didReceiveRemoteNotification的电话:点击通知后,无论该应用程序是否仅在后台运行或根本不运行,因此此答案非常适合我的需求。在iOS 7和8上进行了测试
Newtz

16
就像其他人指出的那样,这不会检测到“从推送通知中启动/打开”。在收到通知时(而不是在打开时)调用此方法。因此,如果您在bg中收到了通知,但点击了应用程序图标以打开应用程序,则此处的代码仍将运行,并且您可能会打开用户不打算打开的页面。
鲍磊

4
@ManuelM。此方法无法告知是否通过通知中心与应用程序图标打开了应用程序,是否选中了后台模式-远程通知。如果未选中,它就会执行。我已经记录在这个岗位的区别:stackoverflow.com/questions/32061897/...
鲍蕾

2
确认这可用于Google Cloud Messaging。
CularBytes

127

晚了,但也许有用

当应用未运行时

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

2
请注意,在以上代码段中,通知不应声明为(UILocalNotification *),而应声明为(NSDictionary *)
cosmix

1
这样一来,您可以查看该应用是否有任何通知,而这些通知仍未运行!问题是,如何检测是否从通知中打开了该应用程序。在这种情况下,即使应用程序根本没有运行,也将调用didReceiveRemoteNotification。-我喜欢您的答案,因为在很多情况下这很重要,但对于问题的正确答案却不是。
Axel Zehden

您的答案和这个答案都在做同一件事吗?
蜂蜜

38

应用启动后,我们无法正确更新视图。这里有复杂的生命周期方法序列,令人感到困惑。

生命周期方法

我们对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

问题

好的,所以现在我们需要:

  1. 确定用户是否通过推送打开应用
  2. 根据推送状态更新视图
  3. 清除状态,以免随后的打开不会使用户回到同一位置。

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

我们的解决方案示意图

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

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

说明

当通过推送打开didFinishLaunchingWithOptionsapplicationWillEnterForeground总是在紧接之前调用时didReceiveRemoteNotification:inactive,因此我们首先在这些方法中重置notificationUserInfo,以确保没有过时的状态。然后,如果didReceiveRemoteNotification:inactive调用,我们就知道我们要从一个推子打开,因此我们设置了self.notificationUserInfo它,然后将其拾取applicationDidBecomeActive以将用户转发到正确的视图。

最后一种情况是用户是否在应用程序切换器中打开了该应用程序(即,在应用程序处于前台时双击主屏幕按钮),然后接收到推送通知。在这种情况下,仅会didReceiveRemoteNotification:inactive被调用,而WillEnterForeground和didFinishLaunching都不会被调用,因此您需要一些特殊的状态来处理这种情况。

希望这可以帮助。


最后,有些东西行得通,谢谢!我想创建一个标志“ appResuming”,并receive在应用程序状态为活动或应用程序正在恢复时在方法中打开屏幕。当应用程序仍处于非活动状态时,这可能导致更改VC的问题。您的解决方案看起来很棒,直到Apple再次更改生命周期。
shelll

关于iOS 9,生命周期方法是否以相同的方式和顺序调用?我已经没有iOS 9设备,因此无法正确测试。
shelll

2
除应用程序切换器外,还有两种边缘情况。1)从顶部拉出通知中心并覆盖应用程序时2)从底部拉出带有wifi / BT / etc的iOS面板并覆盖应用程序时。在这三种情况下,仅applicationWillResignActive调用,然后调用applicationDidBecomeActive。因此,在applicationWillResignActive调用后,请不要保存收到的通知,直到调用applicationDidEnterBackground或为止applicationDidBecomeActive
shelll

感谢您添加这些案例@shelll。它总是变得更加复杂!我不确定iOS9。我想假设它们是相同的可能是安全的,但谁知道。
埃里克·康纳

只是抬头。我今天在测试iOS 11 Beta 9,发现如果您的应用程序处于前台,则锁定手机,然后从锁定屏幕中选择推送通知,它在调用applicationWillEnterForeground之前而不是在调用didReceiveRemoteNotification:background。我们在iOS 10上看到的是它调用applicationWillEnterForeground然后didReceiveRemoteNotification:inactive的情况-因此这是一个尚未涵盖的边缘案例。在我看来,这是iOS代码中的错误,但是鉴于iOS 11版本有多近,这是需要注意的事情。
罗伊

24

这是一个陈旧的帖子……但是它仍然缺少解决该问题的实际方法(如各种注释中所指出的)。

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

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

当接收到通知被调用时再通知是由用户点击。因此,仅查看UIApplicationState用户是否点击它就无法分辨。

此外,您不再需要处理应用程序“冷启动”的情况,application:didFinishLaunchingWithOptions...就像application:didReceiveRemoteNotification...在iOS 9+(也许还有8)中启动后再次被调用一样。

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

斯威夫特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也很好。


1
这似乎是唯一可以区分实际点击通知和在应用程序上打开状态栏的工作解决方案。
liviucmg

4
这是所有StackOverflow中唯一可行的解​​决方案。我唯一要添加的是,当您支持iOS 10或更高版本时,您可以简单地使用UNNotificationCenterAPI,特别是UNNotificationCenterDelegate方法。userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) 仅当用户实际点击通知时,这些API调用func 方法。
DenHeadless,2016年

它如何寻找Swift 3?
JochenÖsterreicher'02

当应用程序处于非活动状态(用户向下滑动通知中心或向上滑动控制中心)并接收通知时,该解决方案不起作用。当用户点击通知时,应用程序未收到applicationWillEnterForeground 呼叫,因此,解决方案无法检测到点击。
DevGansta

@DevGansta当您在中添加课程UNUserNotificationCenter.current().delegateapplication:didFinishLaunchingWithOptions,该应用将userNotificationCenter(didReceive response)在您描述的情况下轻按后调用
Dorian Roy

22

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

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-available内部消息的静默推送aps,因此也请注意这一点:)请参阅https://stackoverflow.com/a/33778990/1418457


2
仅回答不觉得是肮脏的hack和正确的答案。我所缺少的是,如果应用程序在后台并且用户手动将其打开,该如何检查?同时仍然能够检查是否有推送冷启动以及是否从后台推送。
JochenÖsterreicher'17

1
@JochenÖsterreicher嗨,我在这里总结一下,请检查medium.com/@onmyway133/…–
onmyway133

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
}

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

3
请注意,如果在用户位于其他屏幕上时发送通知(例如,如果他们拉下状态栏然后从您的应用接收通知),则“从通知中打开应用”将是误报。
凯文·库珀

4
@凯文没错。这使您想知道为什么苹果似乎将实习生设计为处理通知的过程……
Andreas

我们如何检测是否点击了处于活动状态的通知
Mayank Jain

13

快速:

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

    }

}

4

是的,您可以在appDelegate中通过此方法进行检测:

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

对于本地通知:

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

1
如果应用未运行,则不会调用此方法。多数民众赞成在这里问什么
普菲茨2013年

我的问题不是处理通知,而是知道在单击横幅时(应用处于后台时)它是否已打开。
joao,

3

如果有人想迅速回答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
    }
}

但是如何知道终止应用程序时是否通过点击推送通知来打开应用程序
user3804063 2015年

1
当有人点击推送时,无论应用是否终止,该应用都会打开。.inactive案件正在召集
Hamid Shahsavari

1
我需要检测是否可以通过轻按按钮来打开应用,是否想要导航到相应的内容,我看到instagram了
user3804063

本地通知如何?
阿米尔沙巴尼

3

为Xamarin用户发布。

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

如果是本地通知,则options字典将在其中包含此键 UIApplication.LaunchOptionsLocalNotificationKey

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

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

专业提示:里面UILocalNotification没有标识符,就像是一样UNNotificationRequest。将字典键放在包含requestId的UserInfo中,以便在测试时UILocalNotification,可以使用特定的requestId作为某些逻辑的基础。

我发现即使在iOS 10或更高版本的设备上,当使用UNUserNotificationCenter'和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;
}

2

直接来自文档

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

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

您对这种方法的实现应使用通知来采取适当的措施。

等一下

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

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

相反,您对

application:willFinishLaunchingWithOptions:

要么

application:didFinishLaunchingWithOptions:

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


2

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

使用此图,我们可以看到开发出可在几乎所有可能的用例中运行的强大的通知处理系统的实际要求。

完整的解决方案↓

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

注意:在对Eric的答案的评论中建议使用类似的答案,但是,状态表可以像我在应用程序中一样帮助查找所有可能的情况。

如果未处理任何特殊情况,请在下面找到完整的代码并在下面发表评论:

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感谢您的建议,并用所有详细信息更新了答案
chetan anand

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

1

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

0

您可以使用:

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

处理远程推送通知。

在这里查看文档



0
     // 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的它以某种方式检测到它,如果应用程序是封闭的,即使它重定向到新的岗位或图片或您的个人资料等。
TarvoMäesepp

0

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

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


0

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

    }
}

0

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

0

对于iOS 10+,无论应用程序的状态如何,您都可以使用此方法知道何时单击通知。

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

    //Notification clicked
    completionHandler()
}

0

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
        }

-1

对于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

代表没有成员navigationController
Pablo Cegarra

1
在AppDelegate.h文件中创建一个导航控制器。正在使用它,并且有效!
AAA

-1

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

我希望这有帮助。


-1

当应用程序在后台运行时,您可以使用

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

但是,如果要启动应用程序,并且在关闭应用程序并调试应用程序时,可以转到“ 编辑方案,然后在左侧菜单中选择“运行”,然后在启动中选择“ 等待可执行文件启动,然后在启动应用程序时启动点击推送通知

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.