didReceiveRemoteNotification not called in Background Mode - ios

I am working on push notifications and the data I receive is in JSON format. How can I parse the JSON data, which is shown in the Notification Center below:

if your app in background/foreground mode call this method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
if you used the above method you will face the following error in console
application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
To Resolve this issue
follow the image of steps
if your app in foreground mode call this method
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
choice no-2
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
UIApplicationState state = [application applicationState];
// user tapped notification while app was in background
if (state == UIApplicationStateInactive || state == UIApplicationStateBackground) {
// go to screen relevant to Notification content
} else {
// App is in UIApplicationStateActive (running in foreground)
// perhaps show an UIAlertView
}
}
Swift
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
var state: UIApplicationState = application.applicationState()
// user tapped notification while app was in background
if state == .Inactive || state == .Background {
// go to screen relevant to Notification content
}
else {
// App is in UIApplicationStateActive (running in foreground)
// perhaps show an UIAlertView
}
}

If didReceiveRemoteNotification method is not called in background mode,please follow the below steps
First ON the Push Notification and Tick the check box of Remote Notifications of Background Mode in Capabilities of Target
Then
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void
(^)(UIBackgroundFetchResult))completionHandler
{
if( [UIApplication sharedApplication].applicationState == UIApplicationStateInactive )
{
NSLog( #"INACTIVE" );
}
else if( [UIApplication sharedApplication].applicationState == UIApplicationStateBackground )
{
NSLog( #"BACKGROUND" );
}
else
{
NSLog( #"FOREGROUND" );
}
return YES;
}

Related

Fetch notification data on background without tap the notification

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

How to know if app user click on push notification ios?

I want to detect if user click on push notification to launch the app.or to get it in foreground.
Just implement in your AppDelegate the method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
If the application was not running the didFinishLaunchingWithOptions method gets called when the application starts and you can check the launchOptions parameters like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions != nil) {
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
// Launched from push notification
}
}
}
If the application is already launched you can use this method:
- (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
}
}
You can also check:
Detect if the app was launched/opened from a push notification

iOS silent notification cases

For silent push notification I have implemented this method :
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
if([userInfo[#"aps"][#"content-available"] intValue]== 1) //it's the silent notification
{
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
// CASE - 1
if (state == UIApplicationStateBackground )
{
//Badge increment
}
// CASE -2 for foreground
else {
//Badge increment
}
handler(UIBackgroundFetchResultNewData);
return;
}
Two scenario
1) I want to decrease the badge count when user clicks the notification .(app comes to foreground)
2) and take no action in badge when notification arrives while (app is in foreground ).
But for both the scenarios , same code is executed . And badge decrement occurs . (Commented as CASE - 2).
How to act according to these scenarios .

didReceiveRemoteNotification called twice

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

Why didReceiveRemoteNotification:fetchCompletionHandler is called but usual didReceiveRemoteNotification isn't?

In my application I have two types of push notifications: remote silent notifications with content-available = 1 flag and usual push notifications with body, badge and other stuff.
I also define two delegate methods didReceiveRemoteNotification:fetchCompletionHandler and usual didReceiveRemoteNotification.
But when a push-notification without content-available flag arrives didReceiveRemoteNotification:fetchCompletionHandler is called, instead of didReceiveRemoteNotification.
How to fix this?
Why can't I have two delegate methods for background and usual pushes?
iOS 7 only calls the new one, this is how I handled it in my app:
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
// Pass on
[self application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:nil];
}
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Check if in background
if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive) {
// User opened the push notification
} else {
// User hasn't opened it, this was a silent update
}
}

Resources