how to handle Notification when app State is UIApplicationStateInactive - ios

I am using following code to handle push notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if(application.applicationState == UIApplicationStateInactive)
{
//************************************************************
// I only want this called when user click on notification.
//************************************************************
NSLog(#"Inactive");
if ([[userInfo valueForKey:#"noty_type"] isEqualToString:#"web"])
{
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:[userInfo valueForKey:#"url"]]])
{
dispatch_async(dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[userInfo valueForKey:#"url"]]];
});
}
}
}
if((application.applicationState == UIApplicationStateActive)
{
// I am useing local notification when app in Forground
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.soundName = #"Default";
localNotification.alertBody = [userInfo valueForKey:#"msg"];
localNotification.userInfo = userInfo;
[[UIApplication sharedApplication] scheduleLocalNotification: localNotification];
}
}
This code open the url in safari browser. this code work fine When my app in background and notification come and I click on notification check the applicationState and open the url with [[UIApplication sharedApplication] openURL.
now the scenario that generate the problem.
I open my app .
and Down the notification UI
now Send the notification from server
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler method get called.
Now I get the ApplicationState == UIApplicationStateInactive .
"It open into the safari browser without any user interaction".
I only want this called when user click on notification.
So, How can I handle this condition ?

Try this.
if (application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground)
{
// do something when app open with notification
}

You have to know why the app is inactive. The two possibilities are that the app was in the background and the user is bringing it to the foreground (by tapping on the notification banner), or something like the notification center was pulled down or the user got a system alert.
To tell the difference, keep track of when the user enters the background. Just set a boolean in the applicationDidEnterBackground: delegate method, and clear it when the app is resigning active.
static BOOL didEnterBackground;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Launching the app is kind of like coming from background.
didEnterEnterBackground = YES;
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
didEnterBackground = NO;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
didEnterBackground = YES;
}
Now you can check the variable didEnterBackground to determine how the app got to the inactive state when you're handling a notification.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
if (application.applicationState == UIApplicationStateInactive) {
if (didEnterBackground) {
// The user tapped the notification
}
else {
// The app was inactive, but not in the background.
// Ignore notification, or do whatever the app does
// when receiving a notification while active.
}
}
}

Hope this help you
-(void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
}//iOS below 9
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(nullable NSString *)identifier
forLocalNotification:(nonnull UILocalNotification *)notification
completionHandler:(nonnull void (^)())completionHandler {
// handling local and interactive notifcation
} // iOS 9 and above
Which is used to trigger the notification in background state.

Related

iOS, force quit app first, after tapping the receiving notification banner, app launch failed

I use APNs to send the notifications to my app. But my app does not work well when I did the following steps:
steps
swipe the app to force quit (app is not running, not in background mode ..)
send the notification from APNs
got the notification on my iPhone and I tapped the notification banner
app seemed to try to launch(showed the launch image), but launched fail (crash?)
my app can receive notification foreground and background.
Tap the notification banner in background then it can bring app to foreground then go to the view I wrote, everything works fine.
Except force quit the APP
here is my code in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MFSideMenuContainerViewController *container = [MFSideMenuContainerViewController
containerWithCenterViewController:[self navigationController]
leftMenuViewController:leftMenuViewController
rightMenuViewController:rightMenuViewController];
self.window.rootViewController = container;
[self.window makeKeyAndVisible];
UIMutableUserNotificationAction *acceptAction =
[[UIMutableUserNotificationAction alloc] init];
// Define an ID string to be passed back to your app when you handle the action
acceptAction.identifier = #"MARK_AS_READ_IDENTIFIER";
// Localized string displayed in the action button
acceptAction.title = NSLocalizedString(#"Mark as Read", nil);
// If you need to show UI, choose foreground
acceptAction.activationMode = UIUserNotificationActivationModeBackground;
// Destructive actions display in red
acceptAction.destructive = NO;
// Set whether the action requires the user to authenticate
acceptAction.authenticationRequired = NO;
// First create the category
UIMutableUserNotificationCategory *inviteCategory =
[[UIMutableUserNotificationCategory alloc] init];
// Identifier to include in your push payload and local notification
inviteCategory.identifier = #"actionCategory";
// Add the actions to the category and set the action context
[inviteCategory setActions:#[acceptAction]
forContext:UIUserNotificationActionContextDefault];
// Set the actions to present in a minimal context
[inviteCategory setActions:#[acceptAction]
forContext:UIUserNotificationActionContextMinimal];
NSSet *categories = [NSSet setWithObject:inviteCategory];
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert) categories:categories]];
// for calling didReceiveRemoteNotification when app first launch
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
[self application:application didReceiveRemoteNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
}
return YES;
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
//register to receive notifications
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"Device token: %#",deviceToken);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(#"Fail to get device token: %#", error);
}
// tap the backgraund banner button
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)newUserInfo completionHandler:(void (^)())completionHandler {
if ([identifier isEqualToString:#"MARK_AS_READ_IDENTIFIER"]) {
// when tapping the background banner's button will mark the notification status to read
[Functions updateComingNotificationToRead];
}
if (completionHandler) {
completionHandler();
}
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)newUserInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
//NSLog(#"Notification received: %# %#", newUserInfo,[newUserInfo objectForKey:#"aps"] );
userInfo = newUserInfo;
NSString *alertMessage = [[newUserInfo objectForKey:#"aps"] objectForKey:#"alert"];
UIApplicationState state = [application applicationState];
UIAlertView *alertView = nil;
// for background banner use
switch (state) {
case UIApplicationStateActive: // when app is alive, show alert to notify user
alertView = [[UIAlertView alloc]initWithTitle:NSLocalizedString(#"SmartHome",nil) message:NSLocalizedString(alertMessage,nil) delegate:self cancelButtonTitle:NSLocalizedString(#"Close",nil) otherButtonTitles:NSLocalizedString(#"Open",nil),NSLocalizedString(#"Mark as Read",nil), nil];
[alertView show];
break;
case UIApplicationStateBackground: // app is in background mode
// user tap the banner or tap the mark as read button, code will go here
[Functions addNotificationDataInDatabase:[newUserInfo objectForKey:#"uniqueID"] type:[newUserInfo objectForKey:#"deviceType"] event:[newUserInfo objectForKey:#"event"] time:[newUserInfo objectForKey:#"time"] read:#"0" description:alertMessage];
break;
case UIApplicationStateInactive: // tapping the banner
//NSLog(#"UIApplicationStateInactive");
// go to notification view
// because will go to the notification view detail, set status to read
[self gotoNotificationView:userInfo]; //uniqueID
break;
default:
break;
}
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
// Handle the received message
// Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
handler(UIBackgroundFetchResultNoData);
// send post notification for updating the badge, will get the notification in foreground
[[NSNotificationCenter defaultCenter] postNotificationName:#"APNsNotification" object:self userInfo:nil];
}
Does anyone have this problem before? Did I miss something?
Please help me!!
u can put alert and check launchOptions
if (launchOptions) {
if ([launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"]) {
[self application:self didReceiveRemoteNotification:[launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"] fetchCompletionHandler:^(UIBackgroundFetchResult result) {
}];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)newUserInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) {
NSString *str = [[userInfo objectForKey:#"aps"] objectForKey:#"alert"];
}
}

Issue with implementing badges in ios

I am working in ios app in phonegap with pushnotification.i have added the below code in Appdelegate+notification.m
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
PushPlugin *pushHandler = [self getCommandInstance:#"PushPlugin"];
[pushHandler didFailToRegisterForRemoteNotificationsWithError:error];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(#"didReceiveNotification");
// Get application state for iOS4.x+ devices, otherwise assume active
UIApplicationState appState = UIApplicationStateActive;
if ([application respondsToSelector:#selector(applicationState)]) {
appState = application.applicationState;
}
if (appState == UIApplicationStateActive) {
PushPlugin *pushHandler = [self getCommandInstance:#"PushPlugin"];
pushHandler.notificationMessage = userInfo;
pushHandler.isInline = YES;
[pushHandler notificationReceived];
} else {
//save it for later
self.launchNotification = userInfo;
}
[UIApplication sharedApplication].applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber + [[[userInfo objectForKey:#"aps"] objectForKey: #"badge"] intValue];
}
I am using pushNotification.java file for sending push message to our application in that i have mentioned payload.addBadge(1);
So in my app badge gets update onclick of notification Please suggest me how to do it while receiving notification

Update badge icon when app is in background/suspended mode

I want to update application badge icon when a notification is received. It works fine when app is running (active mode),but having trouble when trying to set it when app is in suspended/background mode. Want to achieve the same without using the 'badge' field in apns dictionary.
Here is what i am doing,
-(void) incrementOneBadge{
NSInteger numberOfBadges = [UIApplication sharedApplication].applicationIconBadgeNumber;
numberOfBadges +=1;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:numberOfBadges];
}
-(void) decrementOneBadge{ NSInteger numberOfBadges = [UIApplication sharedApplication].applicationIconBadgeNumber;
numberOfBadges -=1;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:numberOfBadges];
}
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
}
else {
// Push Notification received in the background
[self incrementOneBadge];
}
[self handlePushNotificationsForState:application withDictionary:userInfo];
}

didReceiveRemoteNotification or didFinishLaunchingWithOptions do not get called in background after APN

I am sending the following APN
{"aps":{"alert":{"body":"Hello sir","action-loc-key":"Caption of the second Button"},"badge":1,"sound":"default","content-available":1},"Key1":"Value1","Key2":"Value2"}
As you see I am setting "content-available":1 as part of payload
When my application is running in foreground, everything work fine. But when my application is running in background and APN Notification comes. Notification display on screen correctly. When I click the icon , it does not call didReceiveRemoteNotification or didFinishLaunchingWithOptions
I do not know what I am missing.
My code is below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
splitViewController.delegate = self;
// Register for Remote Push Notification
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *mySetings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:mySetings];
[application registerForRemoteNotifications];
NSLog(#"didFinishLaunchingWithOptions called");
}
//Accept push Notification when app is not open
NSLog(#"Accept push Notification when app is not open");
NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotif)
{
NSLog(#"Accept push Notification when app is not open if stat ");
[self processRemoteNotificationApplicationStateActive:remoteNotif];
}
return YES;
}
-(void) sendDeviceToken:(NSString *) DeviceToken
{
NSLog(#"sendDeviceToken called");
}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *deviceTokenString = [[[NSString stringWithFormat:#"%#", deviceToken] //convert NSData to NSString with stringWithFormat
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]]stringByReplacingOccurrencesOfString:#" " withString:#""]; // trim the "<>" then remove the spaces
NSLog(#"deviceTokenString : %#", deviceTokenString);
[self sendDeviceToken:deviceTokenString];
NSLog(#"didRegisterForRemoteNotificationsWithDeviceToken called");
}
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(#"Error in registration. Error: %#", error);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"didReceiveRemoteNotification called");
if ( application.applicationState == UIApplicationStateActive)
{
//App is already in the foreground
NSLog(#"App is already in the foreground");
[self processRemoteNotificationApplicationStateActive:userInfo];
}
else
{
NSLog(#"App was just brought from background to foreground");
//App was just brought from background to foreground
[self processRemoteNotificationApplicationStateActive:userInfo];
}
}
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"didReceiveRemoteNotification fetchCompletionHandler called");
if ( application.applicationState == UIApplicationStateActive)
{
//App is already in the foreground
NSLog(#"App is already in the foreground");
[self processRemoteNotificationApplicationStateActive:userInfo];
}
else
{
NSLog(#"App was just brought from background to foreground");
//App was just brought from background to foreground
[self processRemoteNotificationApplicationStateActive:userInfo];
}
completionHandler(UIBackgroundFetchResultNewData);
}
-(void)processRemoteNotificationApplicationStateActive:(NSDictionary *)userInfo
{
[self.myDetailViewController performAPNUpdate];
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
NSLog(#"applicationWillResignActive called");
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(#"applicationDidEnterBackground called");
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
NSLog(#"applicationWillEnterForeground called");
}
- (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(#"applicationDidBecomeActive called");
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSLog(#"applicationWillTerminate called");
}
#pragma mark - Split view
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
if ([secondaryViewController isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]] && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {
// Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return YES;
} else {
return NO;
}
}
When you open the app by tapping on the app icon you do not get any information about the push notification. Apple expects you to sync with your own server in that case.
Thus didReceiveRemoteNotification will never be called. If your app is terminated, didFinishLaunchingWithOptions will be called but you will not get any information about the push notification.
This behaviour does not make much sense to me personally, but to work around it we have created a notification syncing webservice whenever the app is opened.
When you app is running in background and the push with content-available is received, the callback method application:didReceiveRemoteNotification:fetchCompletionHandler: will be called immediately.
When you tap on the alert and the app is opened at this point, no other callback methods will be called. That is an expected behaviour since application:didReceiveRemoteNotification:fetchCompletionHandler: was already called.
It will be weird if your log did not show application:didReceiveRemoteNotification:fetchCompletionHandler: at all.
Also, make sure UIBackgroundModes include remote-notification for silent push.

handleActionWithIdentifier doesn't work

I use UILocalNotification in one app and in iOS 7 or earlier is working fine, but in iOS 8 didReceiveLocalNotification is not being called at all.
After some research I found out that in iOS 8 we have to use handleActionWithIdentifier instead, and so I tried it but also this void is not being called when LocalNotification run.
Here is my code:
I've got this code in didFinishLaunchingWithOptions and it works ok, first time I launched the app I've been asked permission to give notifications etc.
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
Then I got:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
//register to receive notifications
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification
completionHandler:(void(^)())completionHandler
{
NSLog(#"handleAction");
}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"didReceive");
}
When the localNotification run and I slide my finger on the message that appears on the screen nothing happens... logs are not called at all...
I also noticed that if the app is active and running, when the localNotification run, it get called the method didReceiveLocalNotification
Anyone know what am I doing wrong? Thanks for any help
add this to your didFinishLaunchingWithOptions:
if ([[UIApplication sharedApplication]respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
// iOS 8 Notifications
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
// iOS < 8 Notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
You forgot to add this line:
[[UIApplication sharedApplication] registerForRemoteNotifications];
According to the Apple documentation of application:handleActionWithIdentifier:forLocalNotification:completionHandler::
Your implementation of this method should perform the action associated with the specified identifier and execute the block in the completionHandler parameter as soon as you are done. Failure to execute the completion handler block at the end of your implementation will cause your app to be terminated.
Your implementation above is missing the completion handler block.
To make it work, modify your application:handleActionWithIdentifier:forLocalNotification:completionHandler: to be as follows:
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler
{
NSLog(#"handleAction");
if (completionHandler) {
completionHandler();
}
}
This method calle when the user taps a custom action in the alert for a remote or local notification’s.
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandle
{
//some action
}
When you slide finger on screen called this method
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
}
While I see you are handling the "forRemoteNotification:" case,
I don't see this being defined in your original block of code:
application:handleActionWithIdentifier:forLocalNotification:completionHandler:

Resources