I try to get notification data on the background without tap the notification when I open the apps.
I already set Background Modes ON and checked "Background Fetch" and "Remote Notifications" in Capabilities.
and I already call this function
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print(userInfo)
}
but the function not triggered when the notification comes and the function triggered only when I tapped the notification.
my expectation is: I can fetch the notification on the background without tap the notification when I open the app.
In order to configuring your project for Notification background download, enable below capabilities in XCode
don’t forget to set content-available to 1 in the actual notification from your server, to trigger the background download. If you fail to include this flag in the notification, your app will not receive any protocol method calls when the notification arrives.
There are four cases you need to handle:
Case1: Notification arrives, app running in background.
Case2: Notification arrives, app not running in background.
Case3: User taps on notification, app running in background.
Case4: User taps on notification, app not running in background.
Cases 1 and 3 call "didReceiveRemoteNotification" only while cases 2 and 4 provide the notification details in the launch options of "didFinishLaunchingWithOptions" and then call "didReceiveRemoteNotification".
If there is a case like user has killed your app manually, then case 2 will not occur. Your app will never be started in the background to fetch data until after the user chooses to launch it again.
We need to handle all application state cases differently. Below is the code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions {
NSDictionary *data = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (data) {
self.appIsStarting = YES;
}
return YES
}
- (void)applicationWillResignActive:(UIApplication *)application {
self.appIsStarting = NO;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
self.appIsStarting = NO;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
self.appIsStarting = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
self.appIsStarting = NO;
}
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateBackground ||
(state == UIApplicationStateInactive &&
!self.appIsStarting)) {
NSDictionary *aps = userInfo[#”aps”];
if (aps) {
// perform the background fetch and
// call completion handler
}
}
} else if (state == UIApplicationStateInactive &&
self.appIsStarting) {
// user tapped notification
completionHandler(UIBackgroundFetchResultNewData);
} else {
// app is active
completionHandler(UIBackgroundFetchResultNoData);
}
}
Related
I build and app that has Background Modes enabled, and the push notification payload that the app gets has "content-available" key.
This setup results in didReceiveRemoteNotification being called EVERY TIME the app gets a push notification, which means that if i get 3 push notifications while the app is in the background - the function will fire 3 times and the code inside it will be executed when the app will applicationDidBecomeActive
My biggest problem is that there is NO way to know if a user tapped the Push System Alert or tapped the app icon to bring the app from background, since regardless of the user's action, the didReceiveRemoteNotification will fire.
Is there a way to know for sure that the user tapped on the Sys alert?
and this: http://samwize.com/2015/08/07/how-to-handle-remote-notification-with-background-mode-enabled/
and other answers
don't seem to be helpful
For app is background push
- (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
}
}
For app is terminate state
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions != nil) {
// Launched from push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
}
}
My push notification works properly in Normal case but i have problem with following case:
1) when my app remove from background and get notification & tap on app icon then i want to push view controller and display payload data in that view controller.
2 ) when my app in background and get notification & tap on app icon then i want to push view controller and display payload data in that view controller.
following is my userInfo
{
aps = {
alert = "Call from rohan panchal";
appointmentId = 220;
badge = 0;
"call_token" = "T1==cGFydG5lcl9pZD00NTI1ODY1MiZzaWc9MzM1MmM0M2E2MjkwN2JiYWMzNjgyNjk0MjFlZWMyNWEzNTZmZmM3MjpzZXNzaW9uX2lkPTJfTVg0ME5USTFPRFkxTW41LU1UUTNNREl3TVRBd01qVXdOWDV3WXpCRFMyWTRlR2xhUWpGdU1YbFpNamhvV0hoNFVHTi1VSDQmY3JlYXRlX3RpbWU9MTQ3MDIwMTAwMiZyb2xlPXB1Ymxpc2hlciZub25jZT0xNDcwMjAxMDAyLjUyMDM0NDAzNjQzMjMmZXhwaXJlX3RpbWU9MTQ3MDgwNTgwMg==";
doctorId = 238;
"doctor_country" = US;
"doctor_name" = "John smith";
patientId = 239;
"patient_country" = US;
"patient_name" = "Lottry patel";
sessionId = "2_MX40NTI1ODY1Mn5-MTQ3MDIwMTAwMjUwNX5wYzBDS2Y4eGlaQjFuMXlZMjhoWHh4UGN-UH4";
sound = default;
};
}
following my code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// when i remove app from background & click on notification then following code run.
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if(notificationPayload)
{
NSLog(#"%#",notificationPayload);
WebViewController *DashBoard = [[WebViewController alloc]initWithNibName:#"WebViewController" bundle:nil];
self.navcntrl=[[UINavigationController alloc]initWithRootViewController:DashBoard];
}
else
{
DoctorMenuViewController *DoctorVC = [[DoctorMenuViewController alloc]initWithNibName:#"DoctorMenuViewController" bundle:nil];
self.navcntrl=[[UINavigationController alloc]initWithRootViewController:DoctorVC];
}
}
When I got notification then following method called.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"%#",userInfo);
WebViewController *DashBoard = [[WebViewController alloc]initWithNibName:#"WebViewController" bundle:nil];
[self.navcntrl pushViewController:DashBoard animated:YES];
}
Please help.Any help appreciated.
The push notification payload consists of:
alert - the alert string and actions
badge
sound
content-available
The key content-available is a new feature, and it is this key that makes silent push possible.
To enable, you also have to add remote-notifcation as your app UIBackgroundModes as described here.
This is what happens when content-available is in the payload:
If app is Suspended, the system will bring it into Background
If app was killed by user, nothing happens and app remains in Not Running
Read about app state changes.
A potential is pitfall:
You enable with content-available=1. But, it is WRONG to disable with content-available=0. To disable, you have to REMOVE the key in the payload.
plz use this
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if(application.applicationState == UIApplicationStateInactive)
{
NSLog(#"Inactive");
//do your things when you click on notification
}
else if (application.applicationState == UIApplicationStateBackground)
{
NSLog(#"Background");
}
else if (application.applicationState == UIApplicationStateActive)
{
NSLog(#"Active");
}
}
for more information plz read this link http://samwize.com/2015/08/07/how-to-handle-remote-notification-with-background-mode-enabled/
application: didReceiveRemoteNotification:
is called when the OS receives a RemoteNotification and the app is running (in the background/suspended or in the foreground.)
and
application:(UIApplication *)application didFinishLaunchingWithOptions
is called when app is not running and you click on app icon.
1) when my app remove from background and get notification & tap on app icon -
TestViewController *testVC = your_test_vc;
UINavigationController *navVC = [[UINavigationController alloc]initWithRootViewController:testVC];
In TestViewController you can push your controller to show the payload data.
2) Whatever you wrote is correct.
Hope this will help you.
Apps have notification data delivered in a specific set of circumstances. If the user's interaction does not coincide with one of these, the data is not available. There are no exceptions.
the user taps a notification banner (or alert). It does not matter if the app was running or not.
the payload has the content-available field set to a true value, and iOS opts to deliver the notification.
Note: this answer does not explain how to invoke or handle any of the scenarios.
The field value from the userinfo is what you require then under
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground || state == UIApplicationStateInactive)
{ //notification is received when your app is in background
//open the view controller you expected
}
else if(state == UIApplicationStateActive)
{
//notification is received when your app is in foreground
//do nothing
}
}
I have implemented Push Notification in my App.
When my app is in the foreground then my app is working fine.
But when the app is in the background or is killed then my didReceiveRemoteNotification called two times.
I have made a common method for handling Push notification and calling this method from didFinishLaunchingWithOptions and didReceiveRemoteNotification
Here is my Implementation:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *pushDictionary = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (pushDictionary) {
[self customPushHandler:pushDictionary];
}
return YES;
}
And :
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
[self customPushHandler:userInfo];
}
AND:
- (void) customPushHandler:(NSDictionary *)notification {
// Code to push ViewController
NSLog(#"Push Notification"); //Got it two times when Clicked in notification banner
}
When my App is running then ViewController is pushed Once. And when I open my app From notification banner then My screen is pushed twice.
I placed a NSLog in customPushHandler and I got it one time when App is in foreground and Two time when I launch it from Notification banner.
What is issue in my code.?
"When the app is in background, the method is called twice, once when you receive the push notification, and other time when you tap on that notification."
You can verify the application state and ignore the notification received if the app is in background.
Solution: Remote notification method called twice
Swift 4 code:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if (application.applicationState == .background) {
completionHandler(.noData)
return
}
// logic
// call completionHandler with appropriate UIBackgroundFetchResult
}
Check top view controller in current navigation stack like:
if(![self.navigationController.topViewController isKindOfClass:[MyClass class]]) {
//code for pushing new controller
}
check this one condition in didReceiveRemoteNotification, like if you tap on notification while app is in background. By that way, you can avoid getting called twice.
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground || state == UIApplicationStateInactive || state == UIApplicationStateForeground || state == UIApplicationStateActive)
{
//Do checking here.
[self customPushHandler:userInfo];
}
}
Check I have edited my answer.
Whenever you use backgroundFetch for remote notification, add a completion handler for that.
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
[self customPushHandler:userInfo];
completionHandler(UIBackgroundFetchResultNewData)
}
When user taps on a remote notification, the following callback is triggered in the app delegate:
-application:didReceiveRemoteNotification:fetchCompletionHandler:
in this scenario, application is launched and the app state is UIApplicationStateActive which I interpret it as user actioned on a remote notification.
the problem:
This method can also get called when a remote notification arrives and app is in the foreground with inactive state.
example: when notification center view is open(swipe from top edge of screen down) or a UIAlert is open. In both case application state is UIApplicationStateActive and there is no way to tell whether it's a user actioned notification or system push received.
Q: How can I determine if didReceiveRemoteNotification callback is response to user tapping on remote notification vs arrival of remote notification?
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
//When your app is in foreground and it received a push notification
}
else if (state == UIApplicationStateInactive)
{
//When your app was in background and it received a push notification
}
Also, didFinishLaunchingWithOptions will be called if the app was not running and the user tapped a notification. I have not tried it but i can assume you can get the notification details from options.
To differentiate both calls in didReceiveRemoteNotification you can add this code from below.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive) {
[[NSNotificationCenter defaultCenter] postNotificationName:#“inactivePush" object:nil];
}
else if([UIApplication sharedApplication].applicationState==UIApplicationStateActive){
[[NSNotificationCenter defaultCenter] postNotificationName:#"appOpenPush" object:nil];
}
//When the app is in the background
else {
}//End background
}
}
In my app I am using uilocalnotifications. Every thing is ok but one thing. I need to show notifications's alertbody. If app is in foreground state it's fine, but if app is at background state and notification occurs, when i tap on that didReceiveLocalNotification doesn't get called. Obviously didFinishLaunchingWithOptions is also don't called at that time. So what should i do to handle the notification. I am using ios7 and xcode5. Thanks very much in advance if you could help me.
For an app which is not in the foreground, the local notification can subsequently be found in the
-applicationDidFinishLaunchingWithOptions method
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
//Handle local notification here.
}
You can read Apple's documentation for handling notifications here.
If the app is currently in memory, you can check it's state in the following way:
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif
{
if (app.applicationState == UIApplicationStateInactive )
{
NSLog(#"app not running");
}
else if(app.applicationState == UIApplicationStateActive )
{
NSLog(#"app running");
}
}
if application is closed and notification is raised then for that you have to write below code in appdidfinishlaunching method
// Handle launching from a notification
UILocalNotification *objLocalNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (objLocalNotif)
{
NSLog(#"At the time of launching Recieved Notification %#",objLocalNotif);
//Do your stuff here
}
If application is in background and when any local notification is raised the following method of app delegate get called.
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
// Handle the notificaton when the app is running
NSLog(#"Recieved Notification %#",notif);
//do your stuff here
}
Write your code in the below method
- (void)applicationWillEnterForeground:(UIApplication *)application
it will be called when you open your application again which has not been terminated fully but still running in background