ios 检测应用程序是否从推送通知启动/打开
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16393673/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detect if the app was launched/opened from a push notification
提问by joao
Is it possible to know if the app was launched/opened from a push notification?
是否可以通过推送通知知道应用程序是否已启动/打开?
I guess the launching event can be caught here:
我想可以在这里捕获启动事件:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions != nil) {
// Launched from push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
}
}
However, how can I detect it was opened from a push notification when the app was in background?
但是,当应用程序在后台时,如何检测它是从推送通知中打开的?
回答by shanegao
See This code :
请参阅此代码:
- (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
}
}
same as
与...一样
-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
回答by M.Othman
late but maybe useful
迟到但也许有用
When app is not running
当应用程序未运行时
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
is called ..
叫做 ..
where u need to check for push notification
你需要检查推送通知的地方
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");
}
回答by Eric Conner
The issue we had was in correctly updating the view after the app is launched. There are complicated sequences of lifecycle methods here that get confusing.
我们遇到的问题是在应用程序启动后正确更新视图。这里有复杂的生命周期方法序列,令人困惑。
Lifecycle Methods
生命周期方法
Our testing for iOS 10 revealed the following sequences of lifecycle methods for the various cases:
我们对 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
The problem
问题
Ok, so now we need to:
好的,现在我们需要:
- Determine if the user is opening the app from a push
- Update the view based on the push state
- Clear the state so that subsequent opens don't return the user to the same position.
- 确定用户是否通过推送打开应用
- 根据推送状态更新视图
- 清除状态,以便后续打开不会将用户返回到相同位置。
The tricky bit is that updating the view has to happen when the application actually becomes active, which is the same lifecycle method in all cases.
棘手的一点是更新视图必须在应用程序实际激活时发生,这在所有情况下都是相同的生命周期方法。
Sketch of our solution
我们的解决方案草图
Here are the main components of our solution:
以下是我们解决方案的主要组成部分:
- Store a
notificationUserInfo
instance variable on the AppDelegate. - Set
notificationUserInfo = nil
in bothapplicationWillEnterForeground
anddidFinishLaunchingWithOptions
. - Set
notificationUserInfo = userInfo
indidReceiveRemoteNotification:inactive
- From
applicationDidBecomeActive
always call a custom methodopenViewFromNotification
and passself.notificationUserInfo
. Ifself.notificationUserInfo
is nil then return early, otherwise open the view based on the notification state found inself.notificationUserInfo
.
notificationUserInfo
在 AppDelegate 上存储一个实例变量。notificationUserInfo = nil
在applicationWillEnterForeground
和 中设置didFinishLaunchingWithOptions
。- 设置
notificationUserInfo = userInfo
在didReceiveRemoteNotification:inactive
- 从
applicationDidBecomeActive
始终调用自定义方法openViewFromNotification
并传递self.notificationUserInfo
。如果self.notificationUserInfo
为 nil 则提前返回,否则根据中找到的通知状态打开视图self.notificationUserInfo
。
Explanation
解释
When opening from a push didFinishLaunchingWithOptions
or applicationWillEnterForeground
is always called immediately before didReceiveRemoteNotification:inactive
, so we first reset notificationUserInfo in these methods so there's no stale state. Then, if didReceiveRemoteNotification:inactive
is called we know we're opening from a push so we set self.notificationUserInfo
which is then picked up by applicationDidBecomeActive
to forward the user to the right view.
当从推送打开didFinishLaunchingWithOptions
或applicationWillEnterForeground
总是在之前立即调用时didReceiveRemoteNotification:inactive
,因此我们首先在这些方法中重置 notificationUserInfo 以便没有陈旧状态。然后,如果didReceiveRemoteNotification:inactive
被调用,我们知道我们正在从推送中打开,因此我们设置self.notificationUserInfo
然后将其拾取applicationDidBecomeActive
以将用户转发到正确的视图。
There is one final case which is if the user has the app open within the app switcher (i.e. by double tapping the home button while the app is in the foreground) and then receives a push notification. In this case only didReceiveRemoteNotification:inactive
is called, and neither WillEnterForeground nor didFinishLaunching gets called so you need some special state to handle that case.
最后一种情况是,如果用户在应用程序切换器中打开应用程序(即在应用程序处于前台时双击主页按钮),然后接收推送通知。在这种情况下, onlydidReceiveRemoteNotification:inactive
被调用,并且 WillEnterForeground 和 didFinishLaunching 都没有被调用,所以你需要一些特殊的状态来处理这种情况。
Hope this helps.
希望这可以帮助。
回答by MobileVet
This is a well worn post... but it is still missing an actual solutionto the problem (as is pointed out in the various comments).
这是一个陈旧的帖子......但它仍然缺少问题的实际解决方案(正如各种评论中所指出的那样)。
The original question is about detecting when the app was launched/ openedfrom a push notification, e.g.a user taps on the notification. None of the answers actually cover this case.
最初的问题是关于检测何时从推送通知启动/打开应用程序,例如用户点击通知。没有一个答案实际上涵盖了这种情况。
The reason can be seen in the call flow when a notification arrives, application:didReceiveRemoteNotification...
原因可以在通知到达时的呼叫流程中看到, application:didReceiveRemoteNotification...
gets called when the notification is received ANDagain when the notification is tapped by the user. Because of this, you can't tell by just looking at UIApplicationState
wether the user tapped it.
当接收到通知被调用和时再通知是由用户点击。因此,您无法仅通过查看UIApplicationState
用户是否点击它来判断。
Additionally, you no longer need to handle the situation of a 'cold start' of the app in application:didFinishLaunchingWithOptions...
as application:didReceiveRemoteNotification...
is called again after launching in iOS 9+ (maybe 8 as well).
此外,您不再需要处理的应用程序的“冷启动”的情况application:didFinishLaunchingWithOptions...
作为application:didReceiveRemoteNotification...
iOS中推出9+(也许8以及)之后再次调用。
So, how can you tell if the user tap started the chain of events? My solution is to mark the time at which the app begins to come out of the background or cold start and then check that time in application:didReceiveRemoteNotification...
. If it is less than 0.1s, then you can be pretty sure the tap triggered the startup.
那么,如何判断用户点击是否启动了事件链?我的解决方案是标记应用程序开始退出后台或冷启动的时间,然后在application:didReceiveRemoteNotification...
. 如果它小于 0.1 秒,那么您可以非常确定点击触发了启动。
Swift 2.x
斯威夫特 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)
}
}
}
Swift 3
斯威夫特 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)
}
}
}
I have tested this for both cases (app in background, app not running) on iOS 9+ and it works like a charm. 0.1s is pretty conservative too, the actual value is ~0.002s so 0.01 is fine as well.
我已经在 iOS 9+ 上测试了这两种情况(后台应用程序,应用程序未运行),它的工作原理非常棒。0.1s 也相当保守,实际值是 ~0.002s,所以 0.01 也可以。
回答by onmyway133
When app is terminated, and user taps on push notification
当应用程序终止时,用户点击推送通知
public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
print("from push")
}
}
When app is in background, and user taps on push notificaion
当应用程序在后台,并且用户点击推送通知时
If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foregroundso that you can update your user interface and display information pertaining to the notification.
如果用户从系统显示的警报中打开您的应用,系统可能会在您的应用即将进入前台时再次调用此方法,以便您可以更新用户界面并显示与通知相关的信息。
public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if application.applicationState == .inactive {
print("from push")
}
}
Depending on your app, it can also send you silent push with content-available
inside aps
, so be aware of this as well :) See https://stackoverflow.com/a/33778990/1418457
根据您的应用程序,它还可以向您发送带有content-available
inside 的静默推送aps
,因此请注意这一点:) 参见https://stackoverflow.com/a/33778990/1418457
回答by W?odzimierz Wo?niak
Swift 2.0 For 'Not Running' State (Local & Remote Notification)
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
}
回答by Madhu
In application:didReceiveRemoteNotification:
check whether you have received the notification when your app is in the foreground or background.
在application:didReceiveRemoteNotification:
检查您的应用程序在前台或后台时是否收到通知。
If it was received in the background, launch the app from the notification.
如果它是在后台收到的,请从通知中启动应用程序。
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
NSLog(@"Notification received by running app");
} else {
NSLog(@"App opened from Notification");
}
}
回答by LondonGuy
For 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
}
}
回答by Impossible
Yes, you can detect by this method in appDelegate:
是的,您可以在appDelegate 中通过此方法进行检测:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
/* your Code*/
}
For local Notification:
对于本地通知:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
/* your Code*/
}
回答by Wes
Posting this for Xamarin users.
为 Xamarin 用户发布此信息。
The key to detecting if the app was launched via a push notification is the AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)
method, and the options dictionary that's passed in.
检测应用程序是否通过推送通知启动的关键是AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)
方法和传入的选项字典。
The options dictionary will have this key in it if it's a local notification: UIApplication.LaunchOptionsLocalNotificationKey
.
选项字典将在它这个关键,如果它是一个本地通知: UIApplication.LaunchOptionsLocalNotificationKey
。
If it's a remote notification, it will be UIApplication.LaunchOptionsRemoteNotificationKey
.
如果是远程通知,则为UIApplication.LaunchOptionsRemoteNotificationKey
.
When the key is LaunchOptionsLocalNotificationKey
, the object is of type UILocalNotification
.
You can then look at the notification and determine which specific notification it is.
当键为 时LaunchOptionsLocalNotificationKey
,对象的类型为UILocalNotification
。然后,您可以查看通知并确定它是哪个特定通知。
Pro-tip: UILocalNotification
doesn't have an identifier in it, the same way UNNotificationRequest
does. Put a dictionary key in the UserInfo containing a requestId so that when testing the UILocalNotification
, you'll have a specific requestId available to base some logic on.
专业提示:其中UILocalNotification
没有标识符,方式相同UNNotificationRequest
。在包含 requestId 的 UserInfo 中放置一个字典键,以便在测试 时UILocalNotification
,您将有一个特定的 requestId 可用于基于某些逻辑。
I found that even on iOS 10+ devices that when creating location notifications using the UNUserNotificationCenter
's AddNotificationRequest
& UNMutableNotificationContent
, that when the app is not running(I killed it), and is launched by tapping the notification in the notification center, that the dictionary still contains the UILocalNotificaiton
object.
我发现即使在 iOS 10+ 设备上使用UNUserNotificationCenter
's AddNotificationRequest
&创建位置通知UNMutableNotificationContent
时,当应用程序未运行(我杀死它)并通过点击通知中心的通知启动时,字典仍然包含的UILocalNotificaiton
对象。
This means that my code that checks for notification based launch will work on iOS8 and iOS 10+ devices
这意味着我检查基于通知的启动的代码将适用于 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;
}