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
Related
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
{
}
}
}
Running on Xcode 8.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("User accept notification。")
}
else {
print("User don't like receive any notification!")
}
})
let content = UNMutableNotificationContent()
content.title = "Will it rain?"
content.body = "It never rains in (Southern) California!"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "local_notification", content: content, trigger: trigger)
center.add(request)
return true
}
I build and run it on my iPhone and will show a message to ask me whether I like to receive any notification and I click accept.
Then, nothing happen.
I expect it will show a notification from local with some text "Will it rain" like in my code.
You have to make your AppDelegate to adopt UNUserNotificationCenterDelegate
In applicationDidFinishLaunching method, add this center.delegate = self
Then implement this code:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
}
From Apple Docs:
To respond to the delivery of notifications, you must implement a
delegate for the shared UNUserNotificationCenter object. Your delegate
object must conform to the UNUserNotificationCenterDelegate protocol,
which the notification center uses to deliver notification information
to your app. A delegate is required if your notifications contain
custom actions.
Reference:
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SchedulingandHandlingLocalNotifications.html
https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate
My goal is to set a notification that will occur N seconds in the future for the first time, and then repeat every N seconds.
However, creating a repeating notification seems to trigger the UNUserNotificationCenterDelegate immediately.
App delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = self
return true
}
func startRequest() {
let content = UNMutableNotificationContent()
content.body = bodyText
content.categoryIdentifier = categoryIdentifier
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
trigger.nextTriggerDate()
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// This callback received right after the request is made
completionHandler([.alert, .sound])
}
I can work around this by creating a non-repeating notification and then starting repeating notifications when it expires. However, I was hoping there was some way to specify a first trigger date -- if memory serves right, the old notification API had this ability, and perhaps I am misunderstanding this new API
Thanks!
The issue may be that you aren't setting the title on the UNMutableNotificationContent().
content.title = "Title goes Here"
Also, to see if there is an error adding the request, you can add the following code.
center.add(request) { (error : Error?) in
if let theError = error {
// Handle any errors
}
}
I got this from Apple's docs - https://developer.apple.com/reference/usernotifications/unmutablenotificationcontent
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!
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).