I have implemented local notification successfully for iOS 10.2.
But problem is that notification alert only comes if the app is in the background no alert come if the app is foreground.
Is it possible to get local notification in the foreground?
My code is here
func notificationNow(){
print("notification will be triggered in five seconds..Hold on tight")
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()
//To Present image in notification
if let path = Bundle.main.path(forResource: "menu2", ofType: "png") {
let url = URL(fileURLWithPath: path)
do {
let attachment = try UNNotificationAttachment(identifier: "sampleImage", url: url, options: nil)
content.attachments = [attachment]
} catch {
print("attachment not found.")
}
}
// 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 as Any)
}
}
}
In the documentation for UNUserNotificationCenterDelegate:
Important
You must assign your delegate object to the UNUserNotificationCenter object no later before your app finishes launching. For example, in an iOS app, you must assign it in the application(:willFinishLaunchingWithOptions:) or application(:didFinishLaunchingWithOptions:) method.
You appear to be setting the delegate much later — just before the notification is added to the notification center.
I created a simple Swift class singleton with an extension to conform to UNUserNotificationCenterDelegate. In the singleton's init method I assigned the delegate to self. I then initialize the singleton in the AppDelegate's willFinishLaunchingWithOptions method.
Write following code (extension) in the class where you want to observe the local notification
This will notify when you receive notification in foreground or user tapped on notification when the app is in background.
Hope this will solve your problem.
extension ViewController:UNUserNotificationCenterDelegate{
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
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])
// }
}
}
When the app is running in Foreground. You need to capture the Local Notification through delegate methods.
So in your AppDelegate implement the listening for delegate in didFinishLaunchingWithOptions method:
It is important to set the delegate before your app finishes launching.
// Do NOT forget to retain your delegate somewhere
let notificationDelegate = UYLNotificationDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = notificationDelegate
// ...
return true
}
If you want to respond to actionable notifications or receive notifications while your app is in the foreground you need to implement the UNUserNotificationCenterDelegate. This protocol defines two optional methods:
userNotificationCenter(_:willPresent:withCompletionHandler:) is
called when a notification is delivered to a foreground app. You
receive the UNNotification object which contains the original
UNNotificationRequest. You call the completion handler with the
UNNotificationPresentationOptions you want to present (use .none to
ignore the alert).
userNotificationCenter(_:didReceive:withCompletionHandler:) is called
when a user selects an action in a delivered notification. You
receive the UNNotificationResponse object which includes the
actionIdentifier for the user action and the UNNotification object.
The system defined identifiers UNNotificationDefaultActionIdentifier
and UNNotificationDismissActionIdentifier are used when the user taps
the notification to open the app or swipes to dismiss the
notification.
In both cases you must call the completion handler once you finish.
#pragma mark - UNUserNotificationCenterDelegate Methods
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Play sound and show alert to the user
completionHandler([.alert,.sound])
}
Use below method to configure notification to show in foreground,
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
You can refer Apple documentation for more details!
Related
I have an iOS flutter app and I want to display a local notification when the app is in the foreground.
The code I'm going to paste works if the app is in the background, but not when it is open.
Create the notification:
// Create notification content
let content = UNMutableNotificationContent()
content.title = title
content.sound = UNNotificationSound.default
// Set trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
// Submit request
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
In the method:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
}
I am adding the following, driven by other responses to this same problem:
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
None of the above works. The notification is not displayed if the app is in the foreground.
What should I do to display the local notification even if the app is in the foreground?
On iOS the system does not automatically display notifications that are delivered while your app is running in the foreground.
From the documentation
If your app is in the foreground, the system delivers the notification to your app for handling.
You need to implement the userNotificationCenter(_:willPresent:withCompletionHandler:) delegate function and call the provided completion handler with the desired presentation option, probably .banner
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.banner)
}
You will need to establish your Notification Center delegate in an appropriate place, such as didFinishLaunching
I am using didReceiveRemoteNotification for detecting the app notification. But it is is getting fired automatically, when the app is at running state. I need the notification selection get detected when the app is at running state, rather than automatic notification detection through didReceiveRemoteNotification. Thanks in Advance
iOS 10+ provide the custom local notification for handling this type of issue when the app is running in the foreground.
In didFinishLaunchingWithOptions, add the delegate
UNUserNotificationCenter.current().delegate = self
then create an appDelegate extension and add this.
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([.alert,.sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print full message.
print("tap on on forground app",userInfo)
completionHandler()
}
}
For details:
Read This Tutorial
I have setup push notifications using APNS for iOS (Using Swift) in the flutter App. didReceiveRemoteNotification is getting triggered when the app receives a push notification from AWS Pinpoint. But none of the other methods are getting triggered when the user taps the notification. Once I handle the user action on the push notification, I can call a scheme using openUrl to specific route on the app.
I have tried by disabling/enabling background modes in the capabilities in the Xcode. Also verified that push notifications are turned on in the capabilities. Pinpoint uses APNS to send the notification to iOS and Firebase to send the notification to Android which is working fine.
Also tried with iOS target version as 10.0, 11.0 and 12.0
UNUserNotificationCenter.current().delegate = self
is declared in didFinishLaunchingWithOptions and imported UserNotifications
When I tap the push notification, this is getting printed in the console:
Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.
Payload from pinpoint:
[AnyHashable("aps"): {
alert = {
body = "BODY";
title = "TITLE";
};
badge = 0;
"content-available" = 1;
"mutable-content" = 0;
sound = default;
}]
Code,
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print full message.
print(userInfo)
completionHandler()
}
}
didReceive is expected to get triggered when the user taps on the push notification which is not happening in my case.
Can anyone help me on how to trigger when the user taps the push notification?
I was facing a similar problem. iOS swift delegate methods were not triggered. It appears that the methods must have been declared public.
Just replace
func userNotificationCenter
with
public func userNotificationCenter
Maybe it will helps someone...
Have you set center's delegate property?
center.delegate = self
where self is AppDelegate.
If i implement the method to present push notification in ios 10.0
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler(UNNotificationPresentationOptions.alert)
}
then it present all the notification ,
So, my question is how can i prevent to show particular push notification like (login at another deveice ) i just handle code for that particular push only
My push data is
{
"aps" : {
"alert" : {
"id" : 2091
},
"sound" : "chime.aiff"
},
"acme" : "foo"
}
I have used id because i can separate each push by its id, and based on id i can decide weather to show notification in foreground or not..
Thanks #Anbu.Karthik For reference
as per my question we can handle push even user did't tap on notification in
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
print("Handle push from foreground")
let userInfo:[AnyHashable:Any] = notification.request.content.userInfo
let aps:NSDictionary = (userInfo[AnyHashable("aps")] as? NSDictionary)!
let alert:NSDictionary = (aps["alert"] as? NSDictionary)!
let id = Int(alert["id"] as! Int)
if id == your id
{
//Handle push without prompting alert
}
else
{
//Display alert
completionHandler(UNNotificationPresentationOptions.alert)
}
}
And When user tap on notification Following method is called...
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print(userInfo)
}
As per the official documentation:
// The method will be called on the delegate only if the application
is in the foreground. If the method is not implemented or the handler
is not called in a timely manner then the notification will not be
presented. The application can choose to have the notification
presented as a sound, badge, alert and/or in the notification list.
This decision should be based on whether the information in the
notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
So to answer your question, if you want to prevent the notification from being shown, either do not implement this method or do not call the handler.
Hope it helps.
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).