didReceiveRemoteNotification called twice - ios

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

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

How to do some work when push notification arrives?

I have an application, I got the push notifications, but I would like to do that if the device got a push notification my application do some work in the background without user interaction.
Now I can do some work if the user tap on the notification, but it's not enough efficient for me.
1. In Info.plist set UIBackgroundModes to remote-notification
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
2. You need to implement following method in ApplicationDelegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
//Success
handler(UIBackgroundFetchResultNewData);
}
3. Send Push notification with content-available: 1 so it will work like silent
aps {
content-available: 1
alert: {...}
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
{
if (state == UIApplicationState.Background || state == UIApplicationState.Inactive)
{
// Your Logic
}
}
// Write this method in AppDelegate.h or AppDelegate.swift File.
When your push notification arrives, you can do some additional background work on didReceiveRemoteNotification delegate.
On your appDelegate.m file, add below application delegate method with dispatch_async code to run additional background execution.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
//Background execution block
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do some work if the user tap on the notification
});
}

didReceiveRemoteNotification not called in Background Mode

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

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