4

I know there has been a lot written about this topic but I just can't find the right answer.

Is there a way how to know when the user received remote notification and when the user clicked on one on iOS 8. I would like to know this because when I receive it I want to save it and when user clicks on it I want to open some view.

I have found this answer https://stackoverflow.com/a/16393957/1241217 but the problem is when user is in the app and opens notification center and clicks on one, the app is not inactive and not in the background.

I also found this answer https://stackoverflow.com/a/12937568/1241217 but I know that this is ran only when the app is killed and started from new.

I also don't want to do this https://stackoverflow.com/a/32079458/1241217 since I need to detect when I received notification.

So is there a way how to know if the user only clicked on notification. As far as I understood it has to be done in didReceiveRemoteNotification but I don't know how to separate between them. And I need an answer for before iOS 10 because the app target is iOS 8.

MY SOLUTION:

So as I wrote in the comment of Shabbir Ahmad answer my solution was to remember date when the application did become active and the date when the notification was received. If the difference between this dates was a second or less I accepted that as the user clicked on the notification.

schmru
  • 501
  • 1
  • 9
  • 23

2 Answers2

3

You have to implement UNUserNotificationCenterDelegate and its method userNotificationCenter(_:willPresent:withCompletionHandler:) and userNotificationCenter(_:didReceive:withCompletionHandler:) which gets called when a user taps a notification. In willPresent: you have to call the completionHandler with an option that would indicate what should happen when a notification arrives while the app is in foreground.

Registering such a delegate is easy:

UNUserNotificationCenter.current().delegate = self

So e.g.:

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler(UNNotificationPresentationOptions.alert)
}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo
    if let userInfo = userInfo as? [String: Any] {
        // TODO: implement your logic
        // just don't forget to dispatch UI stuff on main thread
    }
}

You can implement that delegate by AppDelegate, but also by any NSObject, I would go with the latter to keep AppDelegate as clean as possible.

P.S.: Of course, this assumes that you have been granted permissions by the user (UNUserNotificationCenter.current().requestAuthorization(options:completionHandler:)) and you are registered to accept notifications (UIApplication.shared.registerForRemoteNotifications()).

Read more in Scheduling and Handling Local Notifications, section Responding to the Delivery of Notifications - while the section is about local notifications, it is exactly the same for the remote ones (they are handled both by the same delegate).

Milan Nosáľ
  • 17,130
  • 3
  • 42
  • 73
  • I have read about UserNotifications but that is for iOS 10+ as far as I understood, but I would like to know if there is a way to do it for iOS before 10. But thanks anyway – schmru Mar 02 '18 at 09:14
2

when you click on notification in background mode before ios 10 and when you are in foreground,in both cases your below method will call,

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)

So you can differentiate the behaviour, First of all you assign a boolean variable in AppDelegate class like this:

class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?
  var isUserTapOnNotification = false

after that make true isUserTapOnNotification in

func applicationWillEnterForeground(_ application: UIApplication) {
        isUserTapOnNotification = tue
    }

because when you tap on notification bar, your app will came in foreground and applicationWillEnterForeground will call first, after that your didReceiveRemoteNotification will call:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

  if #available(iOS 10.0, *) {
//do nothing

}else { //<ios 10
 if isUserTapOnNotification == true {//when app is in background and user tap on notification bar
  //do action whatever you want
} else { //when user is in foreground and notification came,
  //before ios10,notification bar not display in foreground mode,So you can show popup by using userInfo
}

}

after that applicationDidBecomeActive will call and you reset isUserTapOnNotification to false like this:

func applicationDidBecomeActive(_ application: UIApplication) {
       isUserTapOnNotification = false
    }

I hope this answer will help you.

Shabbir Ahmad
  • 587
  • 6
  • 16
  • I was just going to test some similar solution, more in the way of checking the time between didbecomeactive and didreceivenotification and if it is under 2 sec (some estimated time) it was a click. – schmru Mar 02 '18 at 11:08