application: didReceiveRemoteNotification: fetchCompletionHandler: called more than once.How to avoid? - ios

I have an application, that uses Remote/Push notifications.
Background mode for remote notification is used, and everything works as expected.Application wakes up and takes all needed data from server.
But if Push notification alert is not cleared from Notification Center, and user click it application: didReceiveRemoteNotification: fetchCompletionHandler: method will be called again, and that means it will send new request to server, and that is undesirable behaviour.
First option is to check application state, but that causing me a problems, when application is in suspended.I have to do something like code below, but this doesn't work for me:
if([UIApplication sharedApplication].applicationState == UIApplicationStateActive || [UIApplication sharedApplication].applicationState == UIApplicationStateBackground){
}
if([UIApplication sharedApplication].applicationState == UIApplicationStateInactive){
}
Second option is, whether there is a way to clear alerts from Notification Center when user opens application, but I cannot find way to do it.
So is there a way to avoid second call of application:didReceiveRemoteNotification: fetchCompletionHandler: method?

You can send unique ID in your APNS message, then filter second call by remebering that ID - either in your app's temporary collection ( then you are called second time if you kill app between APNS message was received and is clicked ) or persistent storage ( to avoid second call in any case ).

Related

Why iOS silent push triggers application:didFinishLaunchingWithOptions: when app is in background

I have some network requests with 15s timeout in application:didFinishLaunchingWithOptions: method. I found the following odd situation.
T1: app entered background by pressing home button
T2: app received silent push and executed didFinishLaunchingWithOptions: method, then send requests
T3(> T2 + 15s): user taps app icon. All requests in T2 are timeout immediately.
My problem is why didFinishLaunchingWithOptions was triggered in that situation and how to debug(reproduce this situation, because all above are in logs).
didFinishLaunchingWithOptions is called if your app is suspend or killed and you got the notification. Your application might have crashed in background and that's way didFinishLaunchingWithOptions is being called.
To debug that scenario, do the following.
Click on the target and select Edit Scheme
Select Launch to Wait for executable to be launched
Run the app.
Now you can add break points in didFinishLaunchingWithOptions and send notification to your device. Once device receives notification you can do the debugging.
You can check app state to determine whether the app was launched from background when it received a notification:
- (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::
There's nothing to debug here, it is intended functionality.
If you want to handle it differently, you can look for
UIApplicationLaunchOptionsLocalNotificationKey
Inside of launch options, and do whatever work you need to when the app is launched from a notification.
Silent push can launch suspended app (which has been properly killed by iOS system due to memory tight, not the user kill manually) to background. In this case, lifecycle of the app becomes:
application:willFinishLaunchingWithOptions:
application:didFinishLaunchingWithOptions:
applicationDidEnterBackground:
Apple document: About the Background Execution Sequence

didReceiveRemoteNotification no called in background although content_available = true

I use push notification in my app ,It is work fine but I want to receive notification if my app in background but method didReceiveRemoteNotification not called
I mad that
1- enable Background Mod
2- check remote notification
3- put content_available = true in data payload
and also when I test it from fcm dashboard not work ,
Please can anyone help me , Thanks.
If you're testing on iOS 10, content_available must be in JSON value of notification key, not in data key. Although for iOS <= iOS 9, content_available can be in JSON value of data or notification.
There's a typo in your code. It's not "content_available", the correct key is "content-available".
When you send "content-available" = 1, didReceiveRemoteNotification will be called.
You app needs to handle all the possible push notification delivery states:
Your app was just launched
Your app was just brought from background to foreground
Your app was already running in the foreground
You do not get to choose at delivery time what presentation method is used to present the push notification, that is encoded in the notification itself (optional alert, badge number, sound). But since you presumably are in control of both the app and the payload of the push notification, you can specify in the payload whether or not there was an alert view and message already presented to the user. Only in the case of the app is already running in the foreground do you know that the user did not just launch your app through an alert or regularly from the home screen.
You can tell whether your app was just brought to the foreground or not in didReceiveRemoteNotification using this bit of code:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateActive )
// app was already in the foreground
else
// app was just brought from background to foreground
...
}
for further reference. Please check
didReceiveRemoteNotification when in background

Checking whether iOS app is launched from push properly

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.

iOS remote push notification with app background

Looking on web and in many stackoverflow post i have seen that the only way to get remote push notification when the app is in background (and it is launched from icon and not from push message) is to call the server when the app is loaded and get the "last" messages.
I've made this test with instant messaging app (i dont tell the name of the app, but i think you understand):
From another device i've sent one message to my device
When my device has received the notification i have wait for push notification pop-up to disappear.
At this point i have take device offline (no internet connection)
I have then open the application and in the list the message was correctly added
So, if i'm in foreground, i can handle the notification on method didReceiveRemoteNotification.
If the app is in background i can handle the nofification in didFinishLaunchingWithOptions (if i launch it with push notification pop-up).
How it's possible to handle the notification when the application is in background and it is launched from icon and not from the push pop-Up?
Thanks
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
// write code here for handle push notification when app is in foreground
}
else
{
// write code here for handle push notification when app is in background
}
}

Apple Push Notifications To Connect to App

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.

Resources