39

When my App is not running and receives a Push Notification, if I click on that notification, the App is launched - but then it doesn't prompt the user with the Alert-View I set up, asking them whether they want to view the Notification's contents or not. It just launches, and sits there.

The Push Notifications do work perfectly when the App is running - either as the Active app or while in the background - but nothing works correctly when the app is not running.

I tried logging-out the launchOptions NSDictionary in application: didFinishLaunchingWithOptions: to see what load its bringing - but it comes up as "(null)". So It basically contains nothing - which doesn't make sense cause shouldn't it contain the Notification's load?

Anybody have any ideas how to make Push Notifications work when they arrive while the App was NOT running?

I mean how to handle the Push notifications when the App is in not running state. What if, if you receive many notifications & you did not open the app, neither did you tap the system's notification panel. How are you preserving those push for a later retrieval.

Hasmukh D.
  • 393
  • 1
  • 3
  • 5

9 Answers9

41

1) When application is running in background and When application is running in foreground application:didReceiveRemoteNotification: method will called as below.

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    if (application.applicationState == UIApplicationStateInactive)
    {
        // opened from a push notification when the app was on background
        NSLog(@"userInfo->%@", [userInfo objectForKey:@"aps"]);
    }
    else if(application.applicationState == UIApplicationStateActive)
    {
        // a push notification when the app is running. So that you can display an alert and push in any view
        NSLog(@"userInfo->%@", [userInfo objectForKey:@"aps"]);
    }
}

2) When application is not launched (close) then application:didFinishedLaunchingWithOptions method will called.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if (launchOptions != nil)
    {
        // opened from a push notification when the app is closed
        NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
        if (userInfo != nil)
        {
             NSLog(@"userInfo->%@", [userInfo objectForKey:@"aps"]);
        }
    }
    else
    {
        // opened app without a push notification.
    }
}

3) There is no way to remove a specific notification as of. The way to remove all the notifications from your app so they don't show in the Notification Center when the user opens the app from one of them, is to set the app badge to 0.

nyg
  • 2,380
  • 3
  • 25
  • 40
Rahul Patel
  • 5,858
  • 6
  • 46
  • 72
  • Just to add... When app is in background then "- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler" will get call. – SHS Jun 15 '16 at 09:46
  • " The way to remove all the notifications from your app so they don't show in the Notification Center when the user opens the app from one of them, is to set the app badge to 0" was the important piece in this for me as I was tracking a bug where all the notifications were being removed when touching one of them and this help me track it down. Thanks. – chadbag Feb 13 '17 at 20:31
  • 1
    It is not working in iOS 10. We have to implement "-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler" method of UserNotifications framework. – Kirti Nikam Apr 24 '17 at 10:30
  • i have applied this but , application didReceiveRemoteNotification: did not open from a push notification when the app was in background . do you know why ? and what method to use when we want to receive push notification and want to call some procedures while app is in background ? ... i am getting notification messages but could not enter into the procedure . – Moxarth Jul 13 '17 at 10:35
  • You may use "-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler" method to run small piece of code in background when push notification arrives.. You may required to set backgroundmodes for same – Rahul Patel Jul 13 '17 at 11:07
  • i have implemented everything perfectly and i am receiving notifications too . but it never goes inside application:didReceiveRemoteNotification: method unless user taps on notification message . do you have any solution regarding this ? – Moxarth Aug 04 '17 at 11:02
  • 1
    If your app is in foreground then and then it would be called automatically otherwise, respective method would be called when user interact with notification. If you want to call it automatically then you should use "-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler" method to run small piece of code in background when push notification arrives.. You may required to set backgroundmodes for same – Rahul Patel Aug 04 '17 at 11:16
  • How can we debug things in case 2 ? – Shubham Ojha Feb 09 '18 at 06:04
  • @ShubhamOjha : you may put some kind of alert message to debug it. :) – Rahul Patel Feb 09 '18 at 06:23
  • 2
    I have found something at https://stackoverflow.com/questions/37658647/testing-a-closed-ios-app which worked for me – Shubham Ojha Feb 09 '18 at 06:43
11

As per your question, there is no way to hold all the notification when you open the app, better you call an api to get all notification as per time stamp from your back end/server that's how Facebook does.

Vizllx
  • 9,135
  • 1
  • 41
  • 79
8

You can retrieve notifications delivered to your app by using the getDeliveredNotifications(completionHandler:) method. Note that this only returns notifications currently displayed in the Notification Center and not the ones that have been manually cleared by the user.

UNUserNotificationCenter.current().getDeliveredNotifications { notifications in

    // notifications: An array of UNNotification objects representing the local
    // and remote notifications of your app that have been delivered and are still
    // visible in Notification Center. If none of your app’s notifications are
    // visible in Notification Center, the array is empty.

    // As said in the documentation, this closure may be executed in a background
    // thread, so if you want to update your UI you'll need to do the following:
    DispatchQueue.main.sync { /* or .async {} */ 
        // update UI
    }
}
nyg
  • 2,380
  • 3
  • 25
  • 40
7

The app does not process push notification in the background, what it really does the OS is wake up the app once you press in the notification. You can catch this moment in the following way:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    { 

        if ([launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) {
      // Your app has been awoken by a notification...
        }
   }
3

There is no way of handling this on application end. You would need to maintain the unread badge count at the server. When the app is killed the badge value is updated from the server.

So when you open the application any time , you would need to call a web service to get the required notifications and update the badges of tabbar(if used).

neo D1
  • 1,710
  • 13
  • 15
2

Perform actions after notification is received in the terminated state - iOS 13 - Scene Delegate

Recently I came across an issue where I received remote push notification when my app was in the terminated state. In the latest iOS versions Scene delegate is responsible to handle view life cycle methods rather than App Delegate.

Older iOS versions - Handled by App Delegate When the app is terminated and remote push notification is received the payload is reflected in the didfinishLaunchingWithOptions method of App delegate. Using the launch parameter of this method it’s possible to get the payload data and perform any interaction.

New iOS version - Handled by Scene Delegate Similarly when the app is terminated and remote push notification is received the payload is reflected in the scene(willConnectTo session) of the scene delegate. Using the connectingOption parameter of this method it’s possible to get the payload data and perform any interaction.

Hint: To perform any interaction or pass this payload to another view controller once the view controller is set as root view controller keep a delay of some seconds to pass the data.

Example code:

guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
 
if defaults.string(forKey: UserDefaultsKeys.TenantID.rawValue) != nil && connectionOptions.notificationResponse != nil {
   
  let rootViewController = UINavigationController(rootViewController: DashboardVC())
  window?.rootViewController = rootViewController
  window?.makeKeyAndVisible()
   
  DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
    NotificationCenter.default.post(
      name: .passApnsDataToDashboardNotification,
      object: nil,
      userInfo: connectionOptions.notificationResponse?.notification.request.content.userInfo)
  }
   
}
Ajinkya Sonar
  • 38
  • 1
  • 9
0

You can display the alert after launching a previously terminated app from a notification like this:

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { // display the alert }

Bogdan Razvan
  • 1,497
  • 1
  • 16
  • 16
0

For your use case, a BGTask is the best solution which can call the server and get the data and update your database, so that next time, you have available data immediately.

BGTask scheduling is done by OS based on 7 factors (like app usage, batter life, rate limit etc), If your app does not execute any BGTask (you can check easily by saving it to UserDefalts), you can fetch the server data explicitly when the app is live.

Just FYI:

You can configure and Use UserNotificaions to receive a local/remote notification

https://developer.apple.com/documentation/usernotifications?language=objc

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void(^)(void))completionHandler

https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter?language=objc

Ashis Laha
  • 2,176
  • 3
  • 16
  • 17
0

In SceneDelegate :

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let _ = (scene as? UIWindowScene) else { return }
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            if let notificationResponse = connectionOptions.notificationResponse{
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: notificationResponse.notification.request.content.userInfo)
            }
        }
    }