I am working on developing an Enterprise application where maintaining users session synced with device session is a key and required feature.
The basic requirement was to end user session from server as soon as user kills the app, but as there is know way we can get app termination event in code (except starting a background task which can run maximum for 10 mins), server remains unaware about app kill.
I made some workaround to solve this problem.
First, I Used background modes -
a) App downloads content from the network.
b) App downloads content in response to push notifications.
The idea is when user send app in background by switching to another app or by home button or locks device, app is sending a request to the server that app is going in background, and server get to know that app went in background.
-(void)applicationWillResignActive:(UIApplication *)application
{
// notify server that app is in background
}
As soon as server receives the request, it starts the timer for this specific device, which keep on counting apps time spent in background. For every five minutes server sends a request to the device which is in background to get its STATUS.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSDictionary *aps = [userInfo objectForKey:#"aps"];
NSLog(#"NOTIFICATION IN BG %#",[aps objectForKey:#"message"]);
NSString *message = [aps objectForKey:#"message"];
if([message isEqualToString:#"Please update your status"]){
// NOTIFY SERVER THAT APP IS IN BACKGROUND
}
if(application.applicationState != UIApplicationStateBackground){
[self application:application didReceiveRemoteNotification:userInfo];
}
completionHandler(UIBackgroundFetchResultNewData);
}
As soon as app again comes in foreground or become active app notifies server that it has become active.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// notify server that app is in Foreground // server resets the background timer to 0 for corresponding device
}
This approach helps in tracking if app is terminated or not, if server does not get any response from the notification that it sent, it clears the user session from server.
NOW, There are two scenarios in 1st one it works perfectly as expected and in second it doesent.
CASE 1 : App in BackGround but DEVICE NOT LOCKED
App Launched
User logged in
App went in BG
Server has been notified that app is in BG.
Server starts timer.
Elapsed time exceeds 5 mins on server for this device (As app still in BG) .
Server sends notification.
App receives remote push notification in BG.
App notifies its status to server that is in BG (Hence server came to know that it is not terminated and user session should not be cleared off)
This cycle of remote notification and reply to server from device from BackGround goes on till the time either app becomes active again or it is terminated.
Works perfectly fine as expected.
CASE 2 : App in BackGround and DEVICE LOCKED
App Launched
User logged in
App went in BG
Server has been notified that app is in BG.
Server starts timer.
Device is lOCKED
Elapsed time exceeds 5 mins on server for this device (As app still in BG) .
Server sends notification.
Device receives Notification, Device displays banner for notification.
BUT App which is inactive mode does not receives remote push notification in BG.
As a Result App is not able to reply to the server
And whole cycle goes for a toss.
THE ULTIMATE QUESTION IS HOW TO MAKE IT WORK WHEN DEVICE IS LOCKED.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"user info %#",userInfo );
}
by this delegate method you can receive dictionary from server.And can do your work accordingly.This method will call in your app after click on notification in notification tray.
Related
I already implemented this:
enable remote notifications in background modes
silent notification {"aps": {"content-available": 1}}
In Apple docs on Push Notification, they are saying:
When a silent notification arrives, iOS wakes up your app in the
background so that you can get new data from your server or do
background information processing.
#
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
After receive silent notification. In this method, I am downloading some data from the server then data synchronization.
EXPECTED:
The goal is when user open/using the app, it should reflect the latest position for the user.
ACTUAL:
Receive silent notification, download data from server, then data synchronization are works fine when app is running in foreground and background.
But can't wakes up the app in background and to do that when my app is not in running state (app is not launched or killed from app switcher).
QUESTION:
I silent notification doesn't work when app not running? (can't wake up app in background)
Is there missing code in my code which need implemented?
If this way doesn't work when app not running. Is there any way to keep my app local data always same/synced with the database server (when I'm using or not the app)? how other app do it?
Silent notifications are able to bring your app into the background to do any tasks that you may require, as per the Apple documentation:
the system wakes the app in the background (or launches it into the background)
The method you are using, userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:,
is called to let your app know which action was selected by the user for a given notification.
The correct method you should be implementing (even in iOS10) is:
application:didReceiveRemoteNotification:fetchCompletionHandler:. This tells the app that a remote notification arrived that indicates there is data to be fetched.
The way you are doing it is the correct (and probably most popular way) of syncing data between a server and application instance when the user is not actively engaging with the app. Your issue seems to be that you are using the wrong app delegate instance method for your callback.
Update 1:
As per your updated question, I have updated my answer.
That the notifications are not being delivered to your app after it has been terminated by the user is by design. According to the documentation:
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.
To combat this, you can look into Apple PushKit. Originally intended for VoIP apps, it would also work for your application. However unlike regular silent notifications, these do wake up your app even if it has been terminated by the user.
However, keep in mind that once a user terminates an application he expects to have terminated it for good (or until he manually restarts it). To override this would be poor design. Maybe it is fine with the user if the app spends some time downloading the latest data upon launch if the user had terminated the app previously? Depending on the exact reason why you need your app to have the latest information before launch, maybe you don't need to account for this scenario.
We had implemented following method to receive remote notification and enabled "Background Fetch and Remote Notification under Capabilities of a project". Method is hitting even if app is in foreground or background. But "If app is killed, received notification in background then following method doesn't get called". How to fix this?
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Some storage code
}
Advance Thanks for any help !
Killing the app indirectly disables push notifications.
This is because if the user kills the app, the OS will not relaunch it unless the user taps the app icon on the home screen.
This is unfortunate, because most users don't know that killing apps (which is fun!) has these annoying collateral effects.
This is from Apple's "App Programming Guide for iOS":
In most cases, the system does not relaunch apps after they are force quit by the user. One exception is location apps, which in iOS 8 and later are relaunched after being force quit by the user. In other cases, though, the user must launch the app explicitly or reboot the device before the app can be launched automatically into the background by the system.
When an app is killed. Push notification will not support. Because your app is no longer running. So, there is no way to receieve you the push notification.
If the app is not active, this method won't be called. Instead, if the user launches the app from the notification the app will have a payload in didFinishLaunchingWithOptions, in here you can handle the notification.
If the app isn't opened from the notification, you won't get this.
What exactly are you trying to? You can have the app ask for background time, and execute code as needed.
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. =)
I have a Local Notification which fire every minute. Its fire date & other data are taking into the local database. I am used the notification delegate method for notification which gives me notification Active & Background mode.
When Notification fire then update sqlite database every notification (every minute). when notification fire it’s update sqlite database In Application Active State. That’s state working done.
my code like
-(void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification {
// I recieved a notification
if (notification) {
[Update DataBase Method];
}
}
But my problem is when Application goes Background Mode then Database not Update only Notification Received.
How can I update my database its goes in Background mode (or any state) at every notification fire?
Here is the apple site that explain the task in background/suspend mode..
(Recommended) Register for any notifications that report system changes your app needs. When an app is suspended, the system queues key notifications and delivers them when the app resumes execution. Apps should use these notifications to make a smooth transition back to execution. For more information, see “Processing Queued Notifications at Wakeup Time.”
So if app is queuing the notification than you can access this notification data when your app opens next time.
So what you should have to do is that ..
When your app start check that if there is any notification.
If there is than do the database operation according to the data of the notification
Do the Database update task from applicationDidBecomeActive method. applicationDidBecomeActive method will called whenever your app become active.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive.
//If the application was previously in the background, optionally refresh the user interface.
NSLog(#"Dilip applicationDidBecomeActive");
}
I think this will do the work...
Official document from apple developer network mentions that the payload of a push notification is provided to an application when it’s running in foreground, or when it’s activated because of a push notification. I cannot find the statement about what happens when the app is running in background.
I did a test with an instant message application, and found something that I do not understand. Procedure of the test is:
Enable push notification for the app
Switch the app to background
Send two IMs to the client. Two push notification arrives at the client and the badge on the app's icon becomes 2
Shut down cellular network to prevent the app from communicating with its server
Click the app icon to switch it to foreground
After these steps, I can see the 2 messages in the chat window. Because the app is not able to retrieve the messages directly from server, the only explanation is that the push notifications are processed by the app when it’s in background, or they are cached somewhere and can be accessed by the app when it's switched to foreground. But does iOS really allows an app to execute codes when it’s in background, or cache notifications for apps?
The application caches the notifications for 2-5 minutes(i don't know exact) time..
If you receive a notification and you open the app in 2-5 minutes
then in your app delegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
}
You will have the notification object then .. you can get it using launchOPtions object for key UIApplicationLaunchOptionsRemoteNotificationKey
If you don't open it within 2-5 minutes.. there will be no notification object,
This is based on my experience..cant totally support this answer