userNotificationCenter didReceive is not being called when tapping a notification - ios

I have set the following local notification:
let content = UNMutableNotificationContent()
content.title = "Some Title"
content.body = "Some text"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 20, repeats: false)
let request = UNNotificationRequest(identifier: "OneDay", content: content, trigger: trigger)
notificationCenter.add(request)
And I have added the following to AppDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Notification Tapped")
if response.notification.request.identifier == "OneDay" {
print("OneDay Notification Tapped")
}
completionHandler()
}
notificationCenter has been set as:
let notificationCenter = UNUserNotificationCenter.current()
However, none of the above print statements work. The notification is presented, I tap on it, the app is brought to the foreground but nothing is printed to the console.
Am I missing anything here? I've tested in both the simulator and a device. Same result.
I'm working with XCode 11.5, deploying for 12.0.

I'm not sure what is going as I'd have to see the complete code, so I just made a minimal example for you to understand. It also works perfectly fine in the Simulator.
Please feel free to let me know if you have any questions!
First make sure you're asking for the user permission before setting your delegate or scheduling any local notifications, otherwise your notifications will fail silently.
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (isAuthorized, error) in
// ...
}
Once you're done asking authorization (and the status is granted), simply set your delegate:
UNUserNotificationCenter.current().delegate = self
Create your notification:
let content = UNMutableNotificationContent()
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "Body"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "OneDay", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
guard error == nil else {
return
}
// ...
}
And your delegate methods will get called as expected:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// ...
completionHandler([.alert, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.notification.request.identifier == "OneDay" {
// ...
}
completionHandler()
}
}

Adding onto #user13639548 answer:
Make sure to register the current UNUserNotificationCenter.current().delegate each time the app is restarted.
if UserStorage.shared.pushNotificationsToken == nil {
SettingsHelper.getUserPushNotificationStatus(completion: self.processPushNotificationStatus(status:))
} else {
UIApplication.shared.appDelegate?.setAppDelegateAsPushNotificationDelegate()
}
}
This is the resulting call:
func setAppDelegateAsPushNotificationDelegate() {
UNUserNotificationCenter.current().delegate = self
}

When user app into the background and tap on notification.
then the method 'applicationWillEnterForeground' of life cycle is called.
this code is working for me.
func applicationWillEnterForeground(_ application: UIApplication) {
let center = UNUserNotificationCenter.current()
center.delegate = self
}

Related

How to identify UNUserNotificationCenter is came(:didReceive)

Here I use UNUserNotificationCenter to trigger notifications repeatedly. I had a problem when the notification was triggered I wanted to call the function without action. can someone tell me how to do that?
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print(" Foreground Notification IS CALLED ")
completionHandler([.badge, .banner, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print(" Nikan Did recieve calling ")
if response.actionIdentifier == "Okay" {
print(" Notification Clickced ")
}
completionHandler()
}
func createNotification() {
let content = UNMutableNotificationContent()
content.title = "Notification"
content.subtitle = "Wow, Notification"
content.categoryIdentifier = "Actions"
content.sound = UNNotificationSound.defaultRingtone
// show this notification five seconds from now
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
// choose a random identifier
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
// notification actions
let okay = UNNotificationAction(identifier: "Okay", title: "Okay", options: .destructive)
let category = UNNotificationCategory(identifier: "Actions", actions: [okay], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
// UNUserNotificationCenter.current().add(request)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if let error = error {
// Something went wrong
print(error)
}
})
}
I want an answer to call a function when UNUserNotificationCenter's notification is triggered.
Question: How to call a function when a notification is received?
Answer:
When you receive notification (:didReceive), post NSNotification and observe it in your UIViewController.
Call the function in observer's #selecteor method.
DidReceive triggers when tap on push notification, in your case app is is in background and if the app is opening by clicking on push notification, then DidReveive will be triggered.
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let pushInfo = response.notification.request.content.userInfo
// post NSNotification here
// You also have data fetched from push notification (if any) in pushInfo variable
}

Handle local notification when app is in background

Basiccally I'm scheduling a local notification for ringing my alarm at particular time say 8:00 Am. NOw I want to perform a specific task like play sound of alarm when app is in background but without tapping on the notification banner which I recived in the notification list.
I'm using below code, but it works only when i tap the notification banner and get into the app.
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response:
UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
{
print("notification recived in background state")
}
Please help me with the tricky solution to handle my local notification without tapping the banner.
Thanks is Advance.
Setting a sound file which is placed in project bundle
let arrayOfSoundFile = ["water.caf","hello.mp3"]
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationStringForKey("Title..", arguments: nil)
content.body = NSString.localizedUserNotificationStringForKey("Text Here...", arguments: nil)
//Custom sound file as par user requriment select file UIPickerView add and select file
content.sound = UNNotificationSound(named: arrayOfSoundFile[0])
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 50, repeats: false)
let identifier = "WakeUp"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
let center = UNUserNotificationCenter.currentNotificationCenter()
center.addNotificationRequest(request, withCompletionHandler: { (error) in
if error != nil {
print("notification created successfully.")
}
})
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
let userInfo = notification.request.content.userInfo
let dictAps = userInfo["aps"] as! NSDictionary
print(dictAps)
completionHandler([.alert, .badge, .sound])
}

Local Notifications swift 3

I am trying to send a notification on a button click. This is the code in my viewController:
#IBAction func getNotificationButtonPressed(_ sender: Any) {
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Body"
content.categoryIdentifier = "ident"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
let request = UNNotificationRequest(identifier: "ident", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
} else {
print ("success")
}
}
}
also in AppDelegate I have requested permission to use Notifications:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
application.registerForRemoteNotifications()
P.S. I have imported
import UserNotifications
in both AppDelegate and the custom ViewController.
When you create notification at that check
if #available(iOS 10.0, *) {
let content = UNMutableNotificationContent()
content.title = "Intro to Notifications"
content.subtitle = "Lets code,Talk is cheap"
content.body = "Sample code from WWDC"
content.sound = UNNotificationSound.default()
// Deliver the notification in five seconds.
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5.0, repeats: false)
let request = UNNotificationRequest(identifier:requestIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().add(request){(error) in
if (error != nil){
print(error?.localizedDescription)
}
}
}
Implement this delegates method UNUserNotification for Notification being triggered.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Tapped in notification")
}
//This is key callback to present notification while the app is in foreground
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification being triggered")
//You can either present alert ,sound or increase badge while the app is in foreground too with ios 10
//to distinguish between notifications
if notification.request.identifier == requestIdentifier{
completionHandler( [.alert,.sound,.badge])
}
}
Happy coding.

User Notification request always come with default action identifier

I am using the UNUserNotificationCenterDelegate (> ios 10) and one of the delegate methods where I can check the response from the notification has always actionIdentifier equal "com.apple.UNNotificationDefaultActionIdentifier" no matter what I do. The "response.notification.request.content.categoryIdentifier" comes right, with the expected value, but the request.actionIdentifier never comes correctly ("mycustomactionidentifier" in the example below). Does anyone know if I'm missing something?
extension NotificationManager: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Swift.Void) {
completionHandler([.alert,.sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Swift.Void) {
if response.notification.request.content.categoryIdentifier == "TEST" {
if response.actionIdentifier == "mycustomactionidentifier" {
NSLog("it finally works dude!")
}
}
completionHandler()
}
}
I added the action and category to the Notification center:
let uploadAction = UNNotificationAction(identifier: "mycustomactionidentifier", title: "Uploaded", options: [])
let category = UNNotificationCategory(identifier: "TEST", actions: [uploadAction], intentIdentifiers: [])
center.setNotificationCategories([category])
and am sending the request putting the correct identifier:
let uploadContent = UNMutableNotificationContent()
uploadContent.title = String(number) + " asset(s) added"
uploadContent.body = "Check your inventory to manage your assets!"
uploadContent.categoryIdentifier = "TEST"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 6, repeats: false)
let uploadRequestIdentifier = "mycustomactionidentifier"
let uploadRequest = UNNotificationRequest(identifier: uploadRequestIdentifier, content: uploadContent, trigger: trigger)
UNUserNotificationCenter.current().add(uploadRequest, withCompletionHandler: nil)
Firstly: Register your custom actions:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { (granted, error) in
if granted {
// Access granted
} else {
// Access denied
}
}
self.registerNotificationAction()
return true
}
func registerNotificationAction() {
let first = UNNotificationAction.init(identifier: "first", title: "Action", options: [])
let category = UNNotificationCategory.init(identifier: "categoryIdentifier", actions: [first], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
}
And create a content with a unique identifier:
func scheduleNotification() {
// Create a content
let content = UNMutableNotificationContent.init()
content.title = NSString.localizedUserNotificationString(forKey: "Some title", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Body of notification", arguments: nil)
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "categoryIdentifier"
// Create a unique identifier for each notification
let identifier = UUID.init().uuidString
// Notification trigger
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5, repeats: false)
// Notification request
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
// Add request
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
Lastly: Handle the notification with their default and custom actions.
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.notification.request.content.categoryIdentifier == "categoryIdentifier" {
switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier:
print(response.actionIdentifier)
completionHandler()
case "first":
print(response.actionIdentifier)
completionHandler()
default:
break;
}
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .sound])
}
}
Hope it helps!
Second Edition
Here's the results: This is going to be our UNNotificationDefaultActionIdentifier:
And this one is expanded version of the notification, we could handle both actions:
As Mannopson said, you can register a default action identifier.
However I though what you need is another thing :
response.notification.request.identifier
From Apple's actionIdentifier description said This parameter may contain one the identifier of one of your UNNotificationAction objects or it may contain a system-defined identifier. which means you need to register one, hope I am right(as I am a newbie to swift)

Getting local notifications to show while app is in foreground Swift 3

Apparently this is now possible with ios10 :
optional func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void)
This answer basically says the tools needed to do it:
Displaying a stock iOS notification banner when your app is open and in the foreground?
I'm just not really understanding how to put it all together.
I dont know how important this is, but I'm not able to keep the optional func and xcode wants me to switch it to private.
I'm trying to show the badge, and the docs provide
static var badge: UNNotificationPresentationOptions { get }
Little lost here.
And then I'm assuming if I want to exclude a certain view controller from getting these badges and I'm not using a navigation controller this code I found would work? :
var window:UIWindow?
if let viewControllers = window?.rootViewController?.childViewControllers {
for viewController in viewControllers {
if viewController.isKindOfClass(MyViewControllerClass) {
print("Found it!!!")
}
}
}
There is a delegate method to display the notification when the app is open in iOS 10. You have to implement this in order to get the rich notifications working when the app is open.
extension ViewController: UNUserNotificationCenterDelegate {
//for displaying notification when app is in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
//If you don't want to show notification when app is open, do something here else and make a return here.
//Even you you don't implement this delegate method, you will not see the notification on the specified controller. So, you have to implement this delegate and make sure the below line execute. i.e. completionHandler.
completionHandler([.alert, .badge, .sound])
}
// For handling tap and user actions
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case "action1":
print("Action First Tapped")
case "action2":
print("Action Second Tapped")
default:
break
}
completionHandler()
}
}
In order to schedule a notification in iOS 10 and providing a badge
override func viewDidLoad() {
super.viewDidLoad()
// set UNUserNotificationCenter delegate to self
UNUserNotificationCenter.current().delegate = self
scheduleNotifications()
}
func scheduleNotifications() {
let content = UNMutableNotificationContent()
let requestIdentifier = "rajanNotification"
content.badge = 1
content.title = "This is a rich notification"
content.subtitle = "Hello there, I am Rajan Maheshwari"
content.body = "Hello body"
content.categoryIdentifier = "actionCategory"
content.sound = UNNotificationSound.default
// If you want to attach any image to show in local notification
let url = Bundle.main.url(forResource: "notificationImage", withExtension: ".jpg")
do {
let attachment = try? UNNotificationAttachment(identifier: requestIdentifier, url: url!, options: nil)
content.attachments = [attachment!]
}
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 3.0, repeats: false)
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error:Error?) in
if error != nil {
print(error?.localizedDescription ?? "some unknown error")
}
print("Notification Register Success")
}
}
In order to register in AppDelegate we have to write this piece of code in didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
registerForRichNotifications()
return true
}
I have defined actions also here. You may skip them
func registerForRichNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.badge,.sound]) { (granted:Bool, error:Error?) in
if error != nil {
print(error?.localizedDescription)
}
if granted {
print("Permission granted")
} else {
print("Permission not granted")
}
}
//actions defination
let action1 = UNNotificationAction(identifier: "action1", title: "Action First", options: [.foreground])
let action2 = UNNotificationAction(identifier: "action2", title: "Action Second", options: [.foreground])
let category = UNNotificationCategory(identifier: "actionCategory", actions: [action1,action2], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
}
If you want that your notification banner should be shown everywhere in the entire application, then you can write the delegate of UNUserNotificationDelegate in AppDelegate and make the UNUserNotificationCenter current delegate to AppDelegate
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print(response.notification.request.content.userInfo)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .badge, .sound])
}
}
Check this link for more details
https://www.youtube.com/watch?v=Svul_gCtzck
Github Sample
https://github.com/kenechilearnscode/UserNotificationsTutorial
Here is the output
Swift 3 | iOS 10+
Assuming you know how to schedule a local notification:
func scheduleLocalNotification(forDate notificationDate: Date) {
let calendar = Calendar.init(identifier: .gregorian)
let requestId: String = "123"
let title: String = "Notification Title"
let body: String = "Notification Body"
// construct notification content
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: title, arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: body, arguments: nil)
content.sound = UNNotificationSound.default()
content.badge = 1
content.userInfo = [
"key1": "value1"
]
// configure trigger
let calendarComponents: [Calendar.Component] = [.year, .month, .day, .hour, .minute]
let dateComponents = calendar.dateComponents(calendarComponents, from: notificationDate)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
// create the request
let request = UNNotificationRequest.init(identifier: requestId, content: content, trigger: trigger)
// schedule notification
UNUserNotificationCenter.current().add(request) { (error: Error?) in
if let error = error {
// handle error
}
}
}
You need to make your AppDelegate implement the UNUserNotificationCenterDelegate protocol, and set it as the notification center's delegate with UNUserNotificationCenter.current().delegate = self.
// AppDelegate.swift
import UIKit
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// set app delegate as notification center delegate
UNUserNotificationCenter.current().delegate = self
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// called when user interacts with notification (app not running in foreground)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse, withCompletionHandler
completionHandler: #escaping () -> Void) {
// do something with the notification
print(response.notification.request.content.userInfo)
// the docs say you should execute this asap
return completionHandler()
}
// called if app is running in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent
notification: UNNotification, withCompletionHandler completionHandler:
#escaping (UNNotificationPresentationOptions) -> Void) {
// show alert while app is running in foreground
return completionHandler(UNNotificationPresentationOptions.alert)
}
}
Now your local notifications will appear when your app is in the foreground.
See the UNUserNotificationCenterDelegate docs for reference.
Key to getting your notifications to show up while your app is in the foreground is also setting:
content.setValue(true, forKey: "shouldAlwaysAlertWhileAppIsForeground")
in your UNNotificationRequest. As for the rest, see the excellent answer by Rajan Maheshwari.
When your app is open in the foreground userNotificationCenter method call
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler(.alert)
}
None of these answers are good with recent IOS versions
shouldAlwaysAlertWhileAppIsForeground will crash on >= IOS 12
assigning UNUserNotificationCenter.current().delegate changes the behavior of background push notifications. UIApplicationDelegate.didReceiveRemoteNotification() is no longer called, when push notification is received and app is on background (until user clicks the notification).

Resources