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
}
}
Related
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)
}
Is there a way in AppDelegate to get a queue of all the notification data? (Once the user opens the app).
As per apple documentation:
When a remote notification arrives, the system calls the application:didReceiveRemoteNotification:fetchCompletionHandler: method. Notifications usually signal the availability of new information. In your app delegate method, you might begin downloading new data from a server so that you can update your app’s data structures. You might also use the notification to update your user interface.
You have to enable remote notification background mode, only then you will get a callback to the above mentioned method.
Try this in your app delegate:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
For UILocalNotification, you can get received notifications by calling following function in your AppDelegate:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateInactive) {
// Application was in the background when notification was delivered.
}
}
If you want to find the list of local Notifications, that app has already set, check this solution.
You can check for launchOptions Dictionary, for received Notification, when App resumes on tapping a notification,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
NSLog(#"Launch Options:%#",launchOptions);
return YES;
}
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
}
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
}
}
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.