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

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

Related

Launch an app with push notification after it has been terminated

I was wondering if there was a way to wake up an app that has been terminated by the user on ios8-9. By terminated I mean double click on the home button and swipe up.
Is it somehow possible to launch an app by sending a silent push notification so that didreceiveremotenotification gets fired and gives me some runtime ?
I have noticed that a fair share of my users terminate my app. As I rely heavily on background fetch, this a problem. My idea was to send silent push notifications to launch the app in the background and trigger background fetch.
Short Answer: No That is not possible.
Detail:
When there is any new content on server you will send Remote Notification to your application to inform about that. (A Remote Notification is really just a normal Push Notification with the content-available flag set)
When application received this Remote Notification it calls following method:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
In Documentation of this method it is clearly written:
However, the system does not automatically launch your app if the user
has force-quit it. In that situation, the user must relaunch your app
or restart the device before the system attempts to launch your app
automatically again.
Reference:
objc.io: Remote Notifications
Apple Doc about application:didReceiveRemoteNotification:fetchCompletionHandler:

IOS: Apple Push Notifications understanding

Four questions about Push Notifications.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
//app is in foreground I can fetch
} else {
//App is in background, can I still fetch the server? Is there a time limit?
}
}
Using the code above, when the app is in background, can I still fetch the server? Is there a time limit? If the answer is: No, I cannot fetch the server in the background, are there alternatives?
Is there any other benefit of using silent notification besides triggering some method to run within 30 seconds when the app is in the background?
If using silent notification and the app is in the foreground, is the following method still called?
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
completionHandler(UIBackgroundFetchResultNewData);
}
In the payload does "content-available" : 1 calls the method in Q3 (above)? or is there anything else that can trigger the method in Q3?
Yes you can still fetch the server when the app is in background. For many apps, the background state is just a brief stop on the way to the app being suspended.
No this is the main benefit of using silent notification see this link.
Yes application:didReceiveRemoteNotification:fetchCompletionHandler: is called regardless of app state.If app is suspended or not running, then the system wakes up or launches your app and puts it into the background running state before calling the method.
This method is intended for showing the updated content to the user.When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block. If the handler is not called in time, your app will be suspended.
Yes it calls application:didReceiveRemoteNotification:fetchCompletionHandler:, If you want a push notification to wake your app up in the background you need to enable the Remote Notifications capability and then implement the code to handle that remote notification (either by detecting it in the application:didFinishLaunchingWithOptions: if your app is not already running, or by implementing application:didReceiveRemoteNotification:fetchCompletionHandler: in the case your app is already running). In response to the remote notificaiton you would internally trigger your fetching code. Of course you also need to be doing registering for the remote notifications and sending the token to your server. please see this discussion

get data from push notification when app is closed in iOS and app not running in background

When receive a push notification and my application is totally closed, how can handle this info?
Only can get data from NSDictionary on this method didFinishLaunchingWithOptions: or
didReceiveRemoteNotification:
for example: when the user open the application how get data from the push notification?, and not when the user open the push notification directly.
Is there a method that responds and detect if a notification has not been read?
You'll want to implement
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
This will launch your app if needed, then you'll want to save the data somewhere so when the user next's starts the app you grab the data and do something with it.
From Apples Doc's:
Discussion
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a push
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
Just look into the method and I'm certain you'll figure it out :)
I did a quick google, these look like they will help:
SO example: didReceiveRemoteNotification: fetchCompletionHandler: open from icon vs push notification
The first tutorial i saw on it: http://hayageek.com/ios-background-fetch/

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

Will iOS awake my app when i receive silent push notification?(When app is not in running state)

Update Question :
The requirement is; as soon as I receive silent notification, I want to run a web service and show the one liner in the notification bar. It has to work if the app is killed also. any workaround ?
I am trying following method below.
I am new to iOS and i struggled with silent push notification,googled a lot and got stuck.
Will iOS awake my app when i receive silent push notification when app is not launched(i.e when app is removed from app switcher).
my pay load is as
{
aps: {
content-available: 1,
sound: ""
}
}
.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler{
int CA=[[[userInfo valueForKey:#"aps"] valueForKey:#"content-available"] intValue];
if (CA==1) {
my action...
}
completionHandler(UIBackgroundFetchResultNewData);
}
this method is called and works fine when app is in foreground and background.cant awake my app when app is not in running state(i.e app is not launched or killed from app switcher)..
If the App has been removed from the App Switcher, iOS will not awake your app, since the user specifically asked for closing your app.
If the user open your app at least once, and do not remove it from App Switcher, iOS will awake your app
What we have done server-side to handle this is :
If the user's app doesn't connect in the minute after we sent the silent notification, (you can set it as you wish), we send another non-silent push notification to alert the user.
Since the App (is not closed by the user) should automatically fetch data, it should take under a minute.
But for that of course you need a more complex server code than simply sending silent push.
EDIT : (Getting a vote up on this question showed me that it was outdated)
This answer is no longer True... you can now with PushKit wake up your app to do some minor things (like downloading small chunks of data to update content) even if the App has been removed from App Switcher.
Please checkout this:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a remote
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
This clearly says that Using new Background Push feature you can Awake the App Only if Your app is suspended Not if it is terminated forcefully by User.
As far I have tested when the app is terminate by the user (swiping up from the app switcher) you won't have background execution time due to silent push flag (content-available) or background fetch.
Also this:
Also keep in mind that if you kill your app from the app switcher
(i.e. swiping up to kill the app) then the OS will never relaunch the
app regardless of push notification or background fetch. In this case
the user has to manually relaunch the app once and then from that
point forward the background activities will be invoked. -pmarcos (Apple worker)
From apple forums: https://devforums.apple.com/message/873265#873265
You CAN get a PUSH-notification and work with it. I know a little way to do this... Open the AppDelegate.m and and find or put this method - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
After that, put into this method code like mine:
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo) {
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSString *alertType = [apsInfo objectForKey:#"type"]; //my own param in PUSH-notification
globalPushType = alertType; //global variable for working with it in some ViewControllers after app's load
}
I know, this helps a lot of people. =)

Resources