I have implemented application:didReceiveRemoteNotification:fetchCompletionHandler: in my application delegate to respond to push notifications.
When a notification is received while the app is in the background, this method is called immediately and I fetch new data and execute the completion block when done. All as per the documentation. However, if I tap the notification alert this method gets called again, resulting in another network call and a UI update. I would have expected this method to be called once for each push notification, not once on receipt and again on action.
How have others implemented this method?
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[self loadSomeResource:^(NSData *data,NSError *error){
if (error) {
completionHandler(UIBackgroundFetchResultFailed);
}
else if (data){
completionHandler(UIBackgroundFetchResultNewData);
}
else {
completionHandler(UIBackgroundFetchResultNoData);
}
}];
}
Here are the things to be noticed in application:didReceiveRemoteNotification:fetchCompletionHandler: method when you receive a push notification:1. When the app is not launched (i.e, when the app is neither in background nor in foreground), the method is called once and applicationState will be UIApplicationStateInactive.2. When the app is in foreground, the method is called once and applicationState will be UIApplicationStateActive.3. 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. When you receive the push notification, applicationState will be UIApplicationStateBackground and when you tap on that notification, applicationState will be UIApplicationStateInactive.We can ignore it when the applicationState will be UIApplicationStateBackground and hence we can handle the push notification only once for all the three scenarios.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if (application.applicationState == UIApplicationStateBackground) {
completionHandler(UIBackgroundFetchResultNoData);
return;
}
// Do whatever you need here and call completionHandler with appropriate UIBackgroundFetchResult
}
Check your application.applicationState to know if you are in the background or inactive and act accordingly.
Related
Using FCM for push notifications in iOS 10:
This is the snipper that's getting called after pushing a notification via our own API:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
[[FIRMessaging messaging] appDidReceiveMessage:userInfo];
if (userInfo[kGCMMessageIDKey]) {
NSLog(#"Message ID: %#", userInfo[kGCMMessageIDKey]);
}
completionHandler(UIBackgroundFetchResultNewData);
if(application.applicationState == UIApplicationStateBackground){
[[TWMessageBarManager sharedInstance] showMessageWithTitle:#"Bacgkround"
description:#"Wassup"
type:TWMessageBarMessageTypeSuccess callback:^{
}];
}
else if(application.applicationState == UIApplicationStateActive){
[[TWMessageBarManager sharedInstance] showMessageWithTitle:#"Active"
description:#"Wassup"
type:TWMessageBarMessageTypeSuccess callback:^{
}];
}
else if(application.applicationState == UIApplicationStateInactive){
[[TWMessageBarManager sharedInstance] showMessageWithTitle:#"InActive"
description:#"Wassup"
type:TWMessageBarMessageTypeSuccess callback:^{
}];
}
}
When the app is active, above method gets called and UIApplicationStateActive case gets executed and I am showing a pop-up using a 3rd party library. When I press the Home button and push a notification, above method gets called and UIApplicationStateBackground gets executed but I am not sure how do I show a notification in the form of a banner?
Is this the method where I have to handle the notifications? If yes, how do I handle Background and Inactive states?
you just have to put notification body in payload you receive from server, if you don't add it, iOS consider it as silent push notification and will not show notification banner.e.g
{
"data":{
"title":"mytitle",
"body":"mybody",
},
"notification":{
"title":"mytitle",
"body":"mybody",
},
}
when user clicks on notification banner in what ever state application is, you will get its call back method triggered as provided by firebase.e.g.
// [START receive_message]
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
etc.
NOTE: no need to use any third party library to show notification banners as all this is controlled by iOS.
I do not know much about TWMessageBarManager but i can tell you can not user any lib or anything you've made custom to show notification while your app is not in foreground. If your app is in background and you receive a push notification the notification banner will be shown itself by the iOS
This question already has answers here:
Remote notification method called twice
(2 answers)
Closed 7 years ago.
I am trying to implement APNs in my app with the content-available key so that a background refresh will be triggered. Here is my code:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
if([userInfo[#"aps"][#"content-available"] intValue]== 1){
//This stops a refresh happening is a push is delivered with the app in the foreground
if(application.applicationState!=UIApplicationStateActive){
NSLog(#“Background push refresh called");
[self backgroundRefreshWithPushUpdate:NO andHandler:^(BOOL successful, BOOL newMessages) {
if(successful){
if(newMessages) handler(UIBackgroundFetchResultNewData);
else handler(UIBackgroundFetchResultNoData);
}
else{
handler(UIBackgroundFetchResultFailed);
}
}];
}
else handler(UIBackgroundFetchResultNoData);
}
}
I have this additional condition: if(application.applicationState!=UIApplicationStateActive) for refreshing in the background as I don't want it to be triggered if the app is in the foreground. However, if I receive a push and then tap on the notification to open the app ALL of the code in - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler is called again. This means my background fetch is called when the notification first comes in and then it's called again when the notification is tapped on. I don't want this to happen. Any ideas how I can get around this?
Here are the things to be noticed in application:didReceiveRemoteNotification:fetchCompletionHandler: method when you receive a push notification:1. When the app is not launched (i.e, when the app is neither in background nor in foreground), the method is called once and applicationState will be UIApplicationStateInactive.2. When the app is in foreground, the method is called once and applicationState will be UIApplicationStateActive.3. 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. When you receive the push notification, applicationState will be UIApplicationStateBackground and when you tap on that notification, applicationState will be UIApplicationStateInactive.We can ignore it when the applicationState will be UIApplicationStateBackground and hence we can handle the push notification only once for all the three scenarios.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if (application.applicationState == UIApplicationStateBackground) {
completionHandler(UIBackgroundFetchResultNoData);
return;
}
// Do whatever you need here and call completionHandler with appropriate UIBackgroundFetchResult
}
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
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
}
}
I have implemented push notification in my App. When App is in foreground didReceiveRemoteNotification method is get called. But when app is in background this method is not called. Following pattern is used in server side:
{
aps: {
content-available: 1,
sound: "default"
}
}
But still the didReceiveRemoteNotification is not get called. What else is to be done to get triggered the method after push notification arrives.
There's a new API available to handle background events when the device receives a push notification:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
Now, as per the documentation:
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running, the system calls this method
regardless of the state of your app. If your app is suspended or not
running, the system wakes up or launches your app and puts it into the
background running state before calling the method.
When this method is called, your app has up to 30 seconds of
wall-clock time to perform the download operation and call the
specified completion handler block. In practice, your app should call
the handler block as soon as possible after downloading the needed
data. If you do not call the handler in time, your app is suspended.
More importantly, the system uses the elapsed time to calculate power
usage and data costs for your app’s background downloads.
To trigger this method, you notification payload must contain a key content-available:
{
"aps" : {
"content-available" : 1
},
"content-id" : 42
}
Example Code:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"Remote Notification userInfo is %#", userInfo);
NSNumber *contentID = userInfo[#"content-id"];
// Do something with the content ID
completionHandler(UIBackgroundFetchResultNewData);
}
didReceiveRemoteNotification method will be called when push notification arrives in app active state.
If app is inactive when push notification arrived, an option to invoke didReceiveRemoteNotification method is to click on the received notification from notification list and become active.
If app is inactive when push notification arrived and become active by without clicking on notification received, normally there is no way to invoke didReceiveRemoteNotification method.
If you app needs, you can handle it by custom server. Whenever app becomes active, API call can be implemented to list pending notifications.
This would be called:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
Check the launchOptions:
NSDictionary *pushInformation = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(pushInformation)
{
// App opened with push notification
}
didReceiveRemoteNotification Will be called if your app is in UIApplicationStateActive
didReceiveRemoteNotification will be called if your app is inUIApplicationStateBackground or UIApplicationStateInactive and the user opened a push notification from the notification center.
didFinishLaunchingWithOptions will be called upon launch if app was not in the background.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
if (application.applicationState == UIApplicationStateActive )
{
//Your Code here
}
else if (application.applicationState == UIApplicationStateBackground || application.applicationState == UIApplicationStateInactive)
{
//Your code here
}
}