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
});
}
Related
I build and app that has Background Modes enabled, and the push notification payload that the app gets has "content-available" key.
This setup results in didReceiveRemoteNotification being called EVERY TIME the app gets a push notification, which means that if i get 3 push notifications while the app is in the background - the function will fire 3 times and the code inside it will be executed when the app will applicationDidBecomeActive
My biggest problem is that there is NO way to know if a user tapped the Push System Alert or tapped the app icon to bring the app from background, since regardless of the user's action, the didReceiveRemoteNotification will fire.
Is there a way to know for sure that the user tapped on the Sys alert?
and this: http://samwize.com/2015/08/07/how-to-handle-remote-notification-with-background-mode-enabled/
and other answers
don't seem to be helpful
For app is background push
- (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
}
}
For app is terminate state
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions != nil) {
// Launched from push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
}
}
Yes there are also similar questions available in stack, So far, I didn't found any proper concurrent answer from those questions.
How can i download any data or call web-api, When i receive silent push notifications ?
My Code is as below..
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
NSDictionary *dicAPS = [userInfo objectForKey:#"aps"];
if(application.applicationState == UIApplicationStateBackground) {
// This is working..
[self callWebService];
}
else if(application.applicationState == UIApplicationStateInactive)
{
// This is not working..
[self callWebService];
}
else
{
// This is working..
//Show an in-app banner
}
}
Note :
1) From web side, I already added "content-available" as 1.
2) I already added below key in Plist.
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
Hopefully, I'll get new hope from your answer.
Regards.
You are handling it in wrong way.
"didReceiveRemoteNotification" doesn't be call if your app is in InActive State.
Use the following code in your "didFinishLaunchingWithOptions" method in 'AppDelegate' to handle this State.
//Handling PUSH NOtIFICATIONs when app is killed
NSDictionary *pushDic = [[NSDictionary alloc]init];
pushDic = [launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
if (pushDic)
{
[self callWebService];
}
You can't handle any kind of remote or local notification if your app is in INACTIVE mode. App moves to INACTIVE when any other comes into picture like Call, Message or any other.
If you wan to do something needful, you can handle it in applicationDidBecomeActive by the help of any web service.
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)
}
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
}
}