Access Notification Payload if application is launched by local notification - ios

How can I get notified and access the notification payload if a user clicks on a local notification while the app is in the suspended state and launches the application?
Whenever my iOS app is opened from the background state the UNNotification response function is called. The local notification launched by key has been deprecated and I can't figure out how to determine if the app has been launched by a local notification. I need to use the notification payload to populate the view controller properly.
I am using this function to get the notification response while the app is in the background state:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive
response: UNNotificationResponse, withCompletionHandler completionHandler:
#escaping () -> Void) {
setup view here using payload
}

The problem is likely that you are not configuring the UNUserNotificationCenterDelegate early enough. You need to do that very early, as in didFinishLaunching, so that the runtime knows where to send didReceive. This is how I do it:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = self.notifHelper
return true
}

Related

How to check if an iOS app was opened with a push notification (and the data of that notification)?

I can't find any documentation on how to handle this, I'm sending a push notification using firebase messaging with a data payload, title and message. The notification comes through fine but opening the app from background does't trigger any specific options here:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure();
// Override point for customization after application launch.
return true
}
The function doesn't seem to be called.
This is the function called for push notification presses
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
let application = UIApplication.shared
if(application.applicationState == .active){
//Prints the message when user tapped the notification bar when the app is in foreground
print(userInfo)
}
if(application.applicationState == .inactive)
{
print("user tapped the notification bar when the app is in background")
}
completionHandler()
}

userNotificationCenter(_:didReceive:withCompletionHandler:) isn't called

The app is closed. I tap the remote notification banner to launch the app, the userNotificationCenter(_:didReceive:withCompletionHandler:) method should be called.
My app works correctly on the build version, but failed on TestFlight version.
How can I fix this situation?
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
printD(userInfo)
}
As Apple Document about userNotificationCenter(_:didReceive:withCompletionHandler:)
Asks the delegate how to handle a notification that arrived while the
app was running in the foreground.
So when app closed, your app didn't call userNotificationCenter function
Try to catch notification in this case by below code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
// Do your task here
}
}

How to know when app received notification and when user clicked on notification in iOS

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.
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).
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.

In which method should I handle iOS remote notification?

I know similar questions have been asked many times. But it is still very confusing to me after reading those threads, especially after UNUserNotificationCenter is introduced in iOS 10.
The official documentation mentioned 3 methods where I can handle remote notifications:
Implement userNotificationCenter:willPresentNotification:withCompletionHandler: to handle a notification when the app is in foreground.
Implement userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: when the app is in background or not running.
But the documentation also mentioned: In iOS and tvOS, the system delivers the notification payload to the application:didReceiveRemoteNotification:fetchCompletionHandler: method of the app delegate.
So,
To handle a remote notification when app is in background/inactive, should I put my code in application delegate method in 3, or the notificationCenter delegate in 2? Since UNUserNotificationCenter is only available for iOS>10, should I write different code to handle each case?
About 1, it is only available after iOS 10. How can I handle remote notifications when app is running in foreground before iOS 10?
And, more confusing: In case the app is in background, when are the delegate methods called: when the notification message is received? or when the user taps the notification?
Related: iOS push notification: how to detect if the user tapped on notification when the app is in background?
iOS 10 and later:
1) userNotificationCenter willPresent notification: Generally used to decide what to do when user is already inside the app and a notification arrives. You could possibly trigger a remote notification inside the app. After the user taps on the remote notification, method 2 (didReceive response) gets called.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (_ options: UNNotificationPresentationOptions) -> Void) {
//Handle push from foreground
//When a notification arrives and your user is using the app, you can maybe notify user by showing a remote notification by doing this
completionHandler([.alert, .badge, .sound])
//To print notification payload:
print(notification.request.content.userInfo)
}
2) userNotificationCenter didReceive response: Generally used to redirect the user to a particular screen of the app after user taps on the notification.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
//Handle push from background or closed (or even in foreground)
//This method is called when user taps on a notification
//To print notification payload:
print(response.notification.request.content.userInfo)
}
Below iOS 10:
3) application didReceiveRemoteNotification:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
//To print notification payload
print(userInfo)
if #available(iOS 10.0, *) {
}
else {
//Handle remote notifications for devices below iOS 10
if application.applicationState == .active {
//app is currently in foreground
}
else if application.applicationState == .background {
//app is in background
}
else if application.applicationState == .inactive {
//app is transitioning from background to foreground (user taps notification)
}
}
}
4) application didFinishLaunchingWithOptions launchOptions: The only scenario which is left for devices below iOS 10 is when app is closed and user taps on the notification launching the app. You'll have to check the following method for this scenario.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//To print notification payload:
if let notification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? [AnyHashable: Any] {
print(notification)
}
}
LaunchOptions is a dictionary indicating the reason the app was
launched (if any). The contents of this dictionary may be empty in
situations where the user launched the app directly.
Now to answer your questions,
To handle a remote notification when app is in background/inactive, you'll have to add your code in method 2 (userNotificationCenter didReceive response) for devices with iOS 10 and above. Also, you'll have to use method 3 (application didReceiveRemoteNotification) for devices below iOS 10.
To handle remote notifications when app is running in foreground before iOS 10, use the method 3 active state.
In addition to the great answer by Ameya, I wanted to point out that userNotificationCenter:willPresent:notification does not get called if app is in background state.
My complete solution to handle all cases on iOS 10+ would be to also use application:didFinishLaunchingWithOptions:launchOptions, and check if in background state, and handle the notification there too. Your payload, however, now also needs to include the "content-available": 1 field).

Which method is called or How to handle when remote push notification arrives when app is killed manually

App remote notification work well when app is foreground or background state but not works when App is killed manually,
Added background fetch in plist.
Tried with:
NSLog("Do something")
under this method still not able to receive in swift :
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
and
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
When you kill the app manually, the remoteNotifications cannot be handled before the app is launched again.
When a remote notification is present, and you open the app by tapping on that notification, the app launches in the usual manner with the
didFinishLaunchingWithOptions
method being called in the app delegate. But in this case, your notification data would be passed in the 'Options' parameter. You can then check for this parameter and perform the required tasks
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
if (launchOptions != nil)
{
let dictionary:NSDictionary = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as! NSDictionary
self.setNotification(dictionary)
}
return true
}
You cannot handle a push notifications while app is not running. You only can handle when a user open the app form notification.
There is good article about that: How to handle remote notification with background mode enabled

Resources