I am trying to detect if my app was launched from push, and there are tons of threads and answers. They are all wrong in my case, and lead me to writing incorrect behavior. They all tell to write roughly this:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
BOOL wasLaunchedFromPush = NO;
if (application.applicationState == UIApplicationStateInactive) {
wasLaunchedFromPush = YES;
}
...
}
Or this:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
BOOL wasLaunchedFromPush = NO;
if (application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground) {
wasLaunchedFromPush = YES;
}
...
}
When I try this, my app thinks that it was started from a notification when I'm on a phone call, the app is in the background, pretty much always when the app is running but not directly active. When my users return to the app, app acts like they've tapped the notification and opened it, whereas the user hasn't interacted with it at all. I don't know why people accepted that as a correct answer in many questions.
How can I simply check if my app is launched from a notification or not, also regardless of the previous state of the app (running in the background or just launched)?
There is no simple way which is going to tell you that its open from pushNotificaiton specially when app is in active or in background. You have to do some work around,
UIApplicationStateInactive The app is running in the foreground but is
not receiving events. This might happen as a result of an interruption
or because the app is transitioning to or from the background
This is best candidate for the phone call interruption,
You can also add some other work around to check if app ever entered in background or not, if yes then after that if didReceiveRemoteNotification method get called its means its from push notification.
If you only want to know when your app was launched from a remote notification, you should check for this in - application:didFinishLaunchingWithOptions: or - application:willFinishLaunchingWithOptions:. You can check for the presence of UIApplicationLaunchOptionsRemoteNotificationKey in the launchOptions dictionary.
didReceiveRemoteNotification is call when your app is running and a remote notification is received. It is called regardless of any user interaction this is because if the app is running, there will not be a notification (pop up) for the user to interact with.
Checking against UIApplicationStateBackground is definitely wrong. If your push notifications are marked with content-available: 1 then your app is going to be called in that state so that you can perform your content download. This will happen without any user interaction.
The best I know of is using the check against UIApplicationStateInactive alone (i.e. your first code example).
As you say, this doesn't work if the notification arrives while your app has been interrupted by a phone call. That's an interesting problem. You could maybe keep track of call state using CTCallCenter but I've not done that and I don't know if you need VOIP permission to make it work.
Related
I am developing an app which only works in 8.30am to 5.30pm. I want to store the data only in between 8.30am to 5.30 pm. I used local notification for doing so. But it only works when user tap the notification.In 8.30am and 5.30pm, i need to execute some code even if the app is killed. Is there any other mechanism to do so...?
Here is my code:
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification!=nil)
{
[self application:application didReceiveLocalNotification:notification];
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
code to be executed;
}
There is no way to execute a method in your app if the app is killed. You can bring the app up in the background using silent notifications. But silent notifications are better suited for News apps or apps which need to download content in the background so it is readily available for users when the app comes to the foreground.
Apart from this, the only way to execute the method is when it is either in the foreground or at least active in the background (using one of the available background modes). If your app is using a background mode only to stay in the background, Apple will reject the app, so be careful.
My app receiving push notification, and showing appropriate info message for that. However when I'm clicking to the message, application becomes active but application didFinishLaunchingWithOptions is not getting called which is right i think, since the application is not suspended and it just resigns active. The question is how i can make sure that user clicked to message when application becomes to foreground ?
I think what you are looking for is this app delegate method:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
It will be called if your app is backgrounded, and the notification payload will be delivered in the userInfo dictionary. This contrasts with the situation when the app is launched from cold start, when this method does not get called, and instead you check in the launchOptions dictionary for the payload.
However the preferred way to do this since iOS7 is to use this:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;
This method is called when a user taps on a notification, regardless of whether the app is launched from cold start or foregrounded from background. So even if you are not using the completionHandler, it provides a more consistent way of accessing the notification payload. If this method is present, the older one does not get called.
If I understand the question correctly, you are asking how to be sure that the app was brought into the foreground as the result of the user “clicking” i.e. acting on a push notification.
When the app is not running at all, you can use -application:didFinishLaunchingWithOptions: as you mention. The launchOptions dictionary contains the payload, etc. — I won’t describe this since you already know how this works.
When the app IS running however, that method is not going to be called. Instead, - application:didReceiveRemoteNotification: is called. BTW this is called if the app was already in the foreground OR if it was in the background and the user “clicked” on the push notification banner/alert to open the app. It will not be called otherwise: so I believe this is exactly what you’re looking for.
The userInfo dictionary provided by this method will contain the notifications data, similarly to -application:didFinishLaunchingWithOptions: for more ad-hoc processing. (Note: userInfo and launchOptions are semantically different, but hopefully this is obvious. :))
From my understanding, when app is running or in the foreground and a push notification is received, the app should NOT show any alert but the app delegate will call the didReceiveRemoteNotification delegate method and I should handle the push notification in that callback.
The push notification should ONLY display alerts/banners when the app is in the background.
However, our app gets push notification alert with an OK button when the app is running or in the foreground sometime, not all of the time. I'm wondering if it is something new in iOS 7 (I have never heard of this) or is it because I'm using UrbanAirship for push notification for our iOS app using alias of the user. The app will display the push alert when running and run the callback in didReceiveRemoteNotification.
Scratching my head over this. Does anyone know why?
When the App is in foreground, it should not display anything.
If you see alertView, it means you provided code for it.
Something along the following lines:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
//app is in foreground
//the push is in your control
} else {
//app is in background:
//iOS is responsible for displaying push alerts, banner etc..
}
}
If you have implemented pushNotificationDelegate
[UAPush shared].pushNotificationDelegate = self;
then override, and leave it blank
- (void)displayNotificationAlert:(NSString *)alertMessage
{
//do nothing
}
You are most likely seeing the additional push notification because of the way Urban Airship is configured in your code. From Urban Airship's docs:
The sample UI includes an implementation of UAPushNotificationDelegate that handles alerts, sounds, and badges. However, if you wish to customize this behavior, you can provide your own implementation:
[UAPush shared].pushNotificationDelegate = customPushDelegate;
There is more information on the proper way to handle push notifications with Urban Airship in their support articles. Since you are using UA, I would recommend that you use their delegates etc. to handle incoming push notifications while in the foreground rather than implementing your own code in the didReceiveRemoteNotification app delegate method.
Hopefully that helps...if not, please post your code so that we can decipher what is going on. This would be very odd behavior indeed!
This answer is for swift users looking for the same thing
let state = application.applicationState
if (state == .active) {
//app is in foreground
//the push is in your control
} else {
//app is in background:
//iOS is responsible for displaying push alerts, banner etc..
}
Please refer to the checked answer if you guys have any more doubts.
Apple Documentation on handling Notifications
I have code within my app delegate's
application:didReceiveLocalNotification:
method to display an UIAlertView for that local notification, whenever my app is in the foreground.
If my app is within the background when the local notification arrives the user gets presented with the notification and is able to launch the app by selecting it. In this case my app comes to the foreground my App Delegate's
applicationWillEnterForeground:
is called. Afterwards though my didReceiveLocalNotification method is called again, causing an UIAlertView to appear again. But really the user has already had that alert whilst the app was in the background, so ideally I'd like to not display this alert again.
I can see that if an app is launched due to a local notification then within the
application:didFinishLaunchingWithOptions:
method you can inspect the launch options for a key
UIApplicationLaunchOptionsLocalNotificationKey
to know whether or not a local notification caused your app to launch, but there seems to be no such method for finding this out when you are just brought back into the foreground by the user interacting with a local notification.
Checking whether or not my applicationWillEnterForeground method has been called recently would seem a hacky way around this problem, or perhaps something similar to the answers given in this question "iOS how to judge application is running foreground or background?" will allow me to check the
[UIApplication sharedApplication].applicationState
from within my
application:didReceiveLocalNotification:
method. Hopefully it'll be received early enough that my applicationState will still not be set to UIApplicationStateActive in this case.
Or are there any better solutions for this?
Cheers
in AppDelegate you can check state of app when application receives Notification
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIApplicationState state = [application applicationState];
// check state here
if(state ==UIApplicationStateBackground ){
}
}
Just wanted to say that I just noticed the suggested answer of checking on the applicationState has a bit of a bad side effect that it will stop anything happening whilst notification centre is open and over the top of your app. Personally I didn't want this to stop my alert views from being created so i came up with an alternative.
Basically I just record the date when my app was last launched or foregrounded and then whenever testing my notification dates I compare their fireDate with the appLastStarted date and only display the notification if it's occurred since my app's been in the foreground. This fixes the problem with opening the app from a notification, but also allows the alerts to show when the app is not active (ie. behind notification centre).
I've yet to experience any issues with this approach, though admittedly I've only been trying it from today so it's not had a great deal of testing.
Just thought I'd record it unless anyone else had similar requirements.
Swift 4 solution:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
{
if UIApplication.shared.applicationState == .background {
//enter code here
}
completionHandler()
}
I am building an app that has a feature of requiring logins whenever the user goes in and out of the app (seems like overkill, but it is necessary). I am doing this in the applicationDidBecomeActive: method of the AppDelegate, which pops up a login modal view whenever the method is called. The app also creates push notifications, and I recently noticed that whenever the user enters the app via a push notification, they can bypass the whole login process. Not exactly sure if this is some bug in the app or because app entry through push notifications is not calling the applicationDidBecomeActive:.
How do push notifications interact with an app when they are selected?
When an user enter through a push notification, a special method gets called, implement that method to detect if they came from the background:
- (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//came from background, show the login screen
}
}
Instead of applicationDidBecomeActive, use applicationDidEnterBackground.
And then, the password requiere is set when the app minimizes, and not when it comes up again.