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

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
}
}

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()
}

Why userNotificationCenter didReceive is not fired?

This is a part of my code. I want to use UNUserNotificationCenter and UNUserNotificationCenterDelegate to handle notification events.
This code catches the notification event when the app is in a foreground state. But "didReceive" is not fired for a background state.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
print("willPresent") /// This works
completionHandler([.alert, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive") /// This doesn't work
completionHandler()
}
But if I don't use UNUserNotificationCenter.current().delegate = self the delegate method is correctly fired in the background.
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping FetchCompletionHandler) {
print("didReceiveRemoteNotification... \(userInfo)")
}
How can I use "didReceive"? I want to handle the notification in the background.
application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is called when the application receives a silent push notification. Silent push notifications can be delivered in when the application is in the background state, iOS wakes the application to perform background processing invisible to the user.
A silent push notification has the content-available flag set to 1.
Silent push notifications should not include an alert, badge, or sound. Silent push is not meant to be visible to the user, it is only a hint to the application that new remote content is available.
Removing the content-available flag from your push notification payload will cause iOS to handle it as a regular notification. The user notification center delegate methods will be called instead of application(_:didReceiveRemoteNotification:fetchCompletionHandler:) but your application will be unable to do background processing triggered by the notification.
You can validate the content of your push notification payload for problems like this using this tool
This is how I handle push notification in my app. I think you need to implement all of these methods to handle all iOS versions in the foreground and background mode.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound,.alert,.badge], completionHandler: { (granted, error) in
if error == nil {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
})
}
else {
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print(deviceTokenString)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to get token; error: \(error)")
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert,.sound])
//do sth
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
//do sth
}
// for iOS < 10
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
There are methods of UNUserNotificationCenterDelegate:
willPresent: This method gets called when you receive a notification when your app is in the foreground. If the app is in the background then this method will not calls.
didRecieve: This method gets called when a user clicked on the notification.
In case of background state, only 'didRecieve' will be called when user will click on a notification.

How to get userInfo when user click in notification when app is closed?

I'm doing an app that schedule local notifications and save an userInfo. That's part its ok.
But when app is closed, Notification appear, but when user click, the method is not called and I can't handle userInfo.
I saw that there's a new way to receive notification with UNUserNotificationCenter. But is not working too.
That's my implementation in AppDelegate:
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let lNotification = UILocalNotification()
lNotification.userInfo = response.notification.request.content.userInfo
applicationWorker.manage(localNotification: lNotification)
}
Anyone to help me? I saw all the questions related here and didn't found anything.
Thanks!
EDIT:
If someone are looking for a solution, I added UNUserNotificationCenter.current().delegate = self in didFinishLaunchingWithOptions and it worked.
From iOS 10 onwards, the method you mention should be called whether app is opening from background or inactive state. Perhaps you are not accessing the userInfo correctly. The example below works for me.
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let yourData = userInfo["yourKey"] as? String {
// Handle your data here, pass it to a view controller etc.
}
}
Edit: as per the edit to the question, the notification centre delegate must be set in the didFinishLaunching() method, or the method above will not get called.
UNUserNotificationCenter.current().delegate = self
You can got the notification user info from launch option when application open from notification. Here is the code
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if let notification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? [String: AnyObject] {
if let aps = notification["aps"] as? NSDictionary
{
}
}
}

Handling Push Notification on iOS 10.3 or newer when the app is terminated

I want to find out which method is triggered when a terminated application receives a notification from my server. The application is being developed through Swift 4, my Deployment Target is 10.3 and Firebase is used to send notifications to users.
I've configured my application in AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
}
Additionally, I created the extension:
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
}
}
When the app receives a notification and it's running or it's in background, the method "willPresent" is called and when the user taps on the notification and decide to open it, the method "didReceive" is called. No method is triggered if the application is terminated.
Am I doing something wrong?

Why I did not receive any notification on my iPhone?

Running this on Xcode8.3 with swift 3.1
Below is my code in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { granted, error in
if granted {
print("OK!")
}
else {
print("No!")
}
})
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("==== didReceiveRemoteNotification ====")
print(userInfo)
}
I use node-apns to push notification to my app and I can have message from my Debug area in Xcode.
==== didReceiveRemoteNotification ====
[AnyHashable("id"): 123, AnyHashable("aps"): {
alert = "Hello World \U270c";
badge = 3;
}]
But, I did not receive any notification on my iPhone.
Did I missing something?
With iOS 10, you can do the following to see push notifications when the App is in Foreground. You have to implement the below function in AppDelegate. It's a delegate method of UNUserNotificationCenterDelegate.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
Follow the below steps:
Import UserNotifications and extend UNUserNotificationCenterDelegate in AppDelegate.
Set up the UNUserNotificationCenter delegate in didFinishLaunchingWithOptions.
let center = UNUserNotificationCenter.current()center.delegate = self
Implement the will present method in AppDelegate and call the completion handler with the options.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert,.badge,.sound])
}
Now if you get a push, you can see the notification alert even if your app is in foreground. More Info in Apple Doc.
if you want to handle notifications, when your app is active, you should do something like this, because the push view won't appear
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
if application.applicationState == .active {
//create custom view or something
} else {
//it was background/off
}
}
you might be also interested in creating new build schema, so your app will wait until it would be launched, so you can debug your app behaviour when receiving push.

Resources