iOS push notification: how to detect if the user tapped on notification when the app is in background?

101,536

Solution 1

OK I finally figured out.

In the target settings ➝ Capabilities tab ➝ Background Modes, if you check "Remote Notifications", application:didReceiveRemoteNotification: will get triggered as soon as notification arrives (as long as the app is in the background), and in that case there is no way to tell whether the user will tap on the notification.

If you uncheck that box, application:didReceiveRemoteNotification: will be triggered only when you tap on the notification.

It's a little strange that checking this box will change how one of the app delegate methods behaves. It would be nicer if that box is checked, Apple uses two different delegate methods for notification receive and notification tap. I think most of the developers always want to know if a notification is tapped on or not.

Hopefully this will be helpful for anyone else who run into this issue. Apple also didn't document it clearly here so it took me a while to figure out.

enter image description here

Solution 2

I've been looking for the same thing as you and actually found a solution that does not require remote notification to be ticked off.

To check whether user has tapped, or app is in background or is active, you just have to check the application state in

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{

    if(application.applicationState == UIApplicationStateActive) {

        //app is currently active, can update badges count here

    }else if(application.applicationState == UIApplicationStateBackground){

        //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here

    }else if(application.applicationState == UIApplicationStateInactive){

        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here

    }

For more info check:

UIKit Framework Reference > UIApplication Class Reference > UIApplicationState

Solution 3

According to iOS / XCode: how to know that app has been launched with a click on notification or on springboard app icon? you have to check for the application state in didReceiveLocalNotification like this:

if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive)
{
    // user has tapped notification
}
else
{
    // user opened app from app icon
}

Although it does not make totally sense to me, it seems to work.

Solution 4

I ran into this problem, too — but on iOS 11 with the new UserNotifications Framework.

Here for me it is like this:

  • New Launch: application:didFinishLaunchingWithOptions:
  • Received from a terminate state: application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • Received in Foreground: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • Received in Background: userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:

Solution 5

If somebody wants it in swift 3.0

switch application.applicationState {
    case .active:
        //app is currently active, can update badges count here
        break
    case .inactive:
        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here
        break
    case .background:
        //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
        break
    default:
        break
    }

for swift 4

switch UIApplication.shared.applicationState {
case .active:
    //app is currently active, can update badges count here
    break
case .inactive:
    //app is transitioning from background to foreground (user taps notification), do what you need when user taps here
    break
case .background:
    //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
    break
default:
    break
}
Share:
101,536
Bao Lei
Author by

Bao Lei

Updated on July 11, 2022

Comments

  • Bao Lei
    Bao Lei almost 2 years

    There are a lot of stackoverflow threads regarding this topic, but I still didn't find a good solution.

    If the app is not in the background, I can check launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] in application:didFinishLaunchingWithOptions: call to see if it's opened from a notification.

    If the app is in the background, all the posts suggest to use application:didReceiveRemoteNotification: and check the application state. But as I experimented (and also as the name of this API suggests), this method gets called when the notification is received, instead of tapped.

    So the problem is, if the app is launched and then backgrounded, and you know a notification is received from application:didReceiveNotification (application:didFinishLaunchWithOptions: won't trigger at this point), how do you know if user resumed the app from by tapping the notification or just tapping the app icon? Because if the user tapped the notification, the expectation is to open the page mentioned in that notification. Otherwise it shouldn't.

    I could use handleActionWithIdentifier for custom action notifications, but this only gets triggered when a custom action button is tapped, not when the user taps on the main body of the notification.

    Thanks.

    EDIT:

    after reading one answer below, I thought in this way I can clarify my question:

    How can we differentiate these 2 scenarios:

    (A) 1.app goes to background; 2.notification received; 3. user taps on the notification; 4. app enters foreground

    (B) 1.app goes to background; 2.notification received; 3. user ignores the notification and taps on the app icon later; 4. app enters foreground

    Since application:didReceiveRemoteNotification: is triggered in both cases at step 2.

    Or, should application:didReceiveRemoteNotification: be triggered in step 3 for (A) only, but I somehow configured my app wrong so I'm seeing it at step 2?

  • Bao Lei
    Bao Lei over 8 years
    @bhusan have you read the details of my question? I'm seeing didReceiveRemoteNotification getting called when the notification just arrives (before the user taps on it). I wanted to find out whether the user tapped on it.
  • Gottfried
    Gottfried over 8 years
    I have Background Modes set to "OFF" completely, but still get notified when a notification arrives with the app in background mode.
  • Peter Fennema
    Peter Fennema over 8 years
    Great! This helped me. It's a pity however that I need to switch off remote notifications. I would like to prefetch data when a push notification arrives AND detect the user tap. I can't find a way to achieve this.
  • Bao Lei
    Bao Lei over 8 years
    It wouldn't work in this scenario. I tried that before. When that checkbox was checked (see my accepted answer for details), when the notification arrives, your line with comment "// user has tapped notification" will be entered, even though the user didn't tap the notification (the notification just arrived).
  • Werner Kratochwil
    Werner Kratochwil over 8 years
    I disagree. I have "Remote notifications" checked in my background capabilities and it works the way described in my answer. I have "Background fetch" checked as well. Maybe that causes the change?
  • kalim sayyad
    kalim sayyad over 8 years
    I set the Background mode to "ON" mode and enable the remote notifications. Still not able to detect the notification event.
  • Swapnil Dhotre
    Swapnil Dhotre over 8 years
    I have same problem Background mode is set to "ON" and enabled remote notification, but still not get notified when notification arrives to the app in background mode
  • Werner Kratochwil
    Werner Kratochwil over 8 years
    could you please +1 my answer? ;-) I still need need some votes to participate more in stackoverflow. Thanks a lot
  • niks
    niks over 8 years
    @Bao - I think it will cause rejection on Appstore, as this option is basically used to download content related to notification in background. So if you are not downloading any content Apple may reject your app. developer.apple.com/library/ios/documentation/iPhone/Concept‌​ual/…
  • Imran Sh
    Imran Sh about 8 years
    Very useful, Thank you body
  • Afshin
    Afshin about 8 years
    Yupe, this is the solution. tnx.
  • Kevin Cooper
    Kevin Cooper about 8 years
    Note that this will be a false positive if the notification is sent while the user is on a different screen (for example, if they pull down the status bar and then receive a notification from your app).
  • Joe C
    Joe C almost 8 years
    You didn't answer OP's question. He doesn't just want to know that there has been a notification. He wants to know if the user opened the App by tapping that remote notification.
  • Joe C
    Joe C almost 8 years
    This works for me. I had been wondering why sometimes I'd get redundant notifications. It turns out that Werner is correct. If you open the App via the Alert display then you go thru didReceiveRemoteNotification another time, now while the App is UIApplicationStateInactive. Thx Werner.
  • Kostia Kim
    Kostia Kim over 7 years
    What about app being UIApplicationStateInactive as a result of user engaging with Notification centre (swipe from top) or Control centre (swipe from bottom) while the app is on screen. Apparently, there is no way to tell whether APNS was received in Inactive state or user actually tapped it.
  • Dashrath
    Dashrath over 7 years
    On which trigger we should add this code , didReceiveRemoteNotification or didFinishLaunchingWithOptions?
  • Hamid Shahsavari
    Hamid Shahsavari over 7 years
    On didReceiveRemoteNotification
  • mfaani
    mfaani almost 7 years
    @KostiaKim You're right, but that's a super edge case...other than that, this answer is the most valid in terms of separating between app in foreground...user tapping...app in background
  • Nickkk
    Nickkk almost 7 years
    Thanks, this kind of demystifies everything. Remember that in order for the delegate method to be called when the app is in the background, the push notification has to include the content-available key, but then the notification must be silent (i.e. not include a sound or badge) as stated in the official docs.
  • Kiran Jadhav
    Kiran Jadhav almost 7 years
    @Hamid sh,,,, I received push notification in all state i.e. when app is in forground, background, close(terminated)..! but my problem is that how to increment badge count when app is in background state and close (terminate) state???? Please tell me in details how i do it....? my app badge count is only increase when app is in forground state.....? if possible please tell me in brief.......!
  • Moxarth
    Moxarth almost 7 years
    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
    Moxarth almost 7 years
    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 ?
  • DatForis
    DatForis over 6 years
    @KostiaKim I was actually able to fix the issue of not knowing whether the control center was open or if the user actually tapped the notification by adding a boolean that is set to true in the func applicationDidEnterBackground(_ application: UIApplication) and false in the func applicationDidBecomeActive(_ application: UIApplication) this allowed me to show the in app notifications when the app is inactive due to the control center or the notifications list
  • fabb
    fabb over 6 years
    With a minimum deployment target of iOS 10, this is no longer necessary, as the new UserNotificationCenter api does not suffer from these pesky problems anymore.
  • Allan Nienhuis
    Allan Nienhuis over 6 years
    it boggles my mind why Apple didn't present a different event or simply add a flag in the metadata to indicate that the user actually interacted with the notification. Having to 'deduce' whether the user took an action based on ambient information about the application state is pretty unreasonable for a key bit of information that affects the user's expectation of application behaviour. Perhaps the only thing to do is to create a custom action to open the app with that information?
  • sudo
    sudo over 6 years
    I saw that Apple added this but couldn't figure out how to get the notification's custom payload this way.
  • sudo
    sudo over 6 years
    The problem is this delegate function also gets called if you receive a notification while in the app.
  • Ryan
    Ryan over 5 years
    This is the official way which Apple says. According to the Apple's doc, it will be called whenever user interact with the push notification UI. If app is not in the background, it will launch the app in background mode and call this method.
  • dead_can_dance
    dead_can_dance over 4 years
    Note in iOS10+ this approach is no longer necessary a UNUserNotificationCenterDelegate method can now be used instead which is called when a user interacts with a notification. You can also present stock notification alerts when your app is in the foreground as well. See stackoverflow.com/a/41783666/1761357 for detailed explanation.
  • dead_can_dance
    dead_can_dance over 4 years
    This is the correct answer for iOS 10+. Full explanation provided on another thread here: stackoverflow.com/a/41783666/1761357.
  • Nikhil Muskur
    Nikhil Muskur over 4 years
    This is function is called when the app is in a background state and becomes active and also when the app starts from the killed state
  • OhadM
    OhadM almost 4 years
    This is the way to do it in iOS 11 +
  • Zorayr
    Zorayr almost 4 years
    @NikhilMuskur only if "remote notifications" are enabled yeh?
  • Zorayr
    Zorayr almost 4 years
    This is just so complicated 🤦‍♂️
  • Claudiu
    Claudiu about 3 years
    This is working when interacting with a single notification at a time, what about the case where the user removes all notifications, is there a way to find out this?
  • Erkki Nokso-Koivisto
    Erkki Nokso-Koivisto almost 3 years
    duplicate to Yatin Arora / Aleks
  • Skywalker
    Skywalker almost 3 years
    @ErkkiNokso-Koivisto I used their answer, but you really can't call it an exact duplicate. This is a much more detailed answer.
  • Skywalker
    Skywalker almost 3 years
    @Claudiu I haven't tried that. Could you try to dismiss all and print the actionIdentifier? If its not calling the print, it means this method doesn't get called on this action. :(
  • Danilo
    Danilo about 2 years
    just a note; use < 1000.0 for 1 sec why timeIntervalSince returns in ms
  • bauerMusic
    bauerMusic about 2 years
    @Danilo Are you sure? From what I know, timeIntervalSince returns in seconds. Documentation are unhelpful: " interval between the date object and ..." . Can you point me so a source?
  • Muhammed Refaat
    Muhammed Refaat almost 2 years
    This answer helped me a lot, but just one question, what if I want to detect if I clicked the notification while the app is already open (as in that case the state will be 'active')