I am trying to schedule notifications in my project. I don't get any errors or runtime crashes, however for some reason I don't get any notifications on the simulator when scheduling. I am not sure where exactly I am going wrong, as I have been successful in doing so in a previous project.
Does anyone have any suggestions ?
import UserNotifications
func addNotification(title: String, category: String, UI: String, date: Date) {
// Remove Notification
removeNotifcation(UI: UI)
// Create Notification
// iOS 10 Notification
if #available(iOS 10.0, *) {
let notif = UNMutableNotificationContent()
notif.title = title
notif.subtitle = "Reminder for \(title)"
notif.body = "Your Reminder for \(title) set for \(date)"
notif.sound = UNNotificationSound.default()
notif.categoryIdentifier = UI
let today = Date()
let interval = date.timeIntervalSince(today as Date)
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: false)
let request = UNNotificationRequest(identifier: title, content: notif, trigger: notifTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if error != nil {
print(error as Any)
// completion(Success: false)
} else {
//completion(Sucess: true)
}
})
} else {
let newNotification:UILocalNotification = UILocalNotification()
newNotification.category = category
newNotification.userInfo = [ "UUID" : UI]
newNotification.alertBody = "Your reminder for \(title)"
newNotification.fireDate = date
//notification.repeatInterval = nil
newNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.shared.scheduleLocalNotification(newNotification)
}
}
func removeNotifcation(UI: String) {
//Remove Notif
//iOS 10 Notification
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [UI])
} else {
//Remove Notif
let app:UIApplication = UIApplication.shared
for oneEvent in app.scheduledLocalNotifications! {
let notification = oneEvent as UILocalNotification
let userInfoCurrent = notification.userInfo!
let uuid = userInfoCurrent["UUID"] as! String
if uuid == UI {
//Cancelling local notification
app.cancelLocalNotification(notification)
break;
}
}
}
}
This is how I am calling the methods
dataManager.addNotification(title: "String", category: "001", UI: uuid, date: datePicker.date)
Notifications don't appear when app is in foreground only if you implement willPresent delegate method and because of
let today = Date()
this date makes it triggers when you run it , so add a time interval and send app to background and you'll see it , plus make sure you request a permission for it in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
print("granted: (\(granted)")
}
return true
}
Also you can have a look to this tutorial
//
Edit : to verify it triggers anyway implement this
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("handling notifications with the TestIdentifier Identifier")
completionHandler()
}
and set the delegate to the VC
UNUserNotificationCenter.current().delegate = self
Related
I'm trying to implement local notifications within my app. The idea is that the user is sent a notification every day at 9:00 a.m. with a different quote, but I encountered a bug where the same content of the notification is always shown, i.e. the same quote is repeated endlessly. How could I fix it? This is the code I'm using, and I tried to use a UUID for every notification that is sent, but it didn't bring improvements.
let notificationCenter = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .sound]
notificationCenter.requestAuthorization(options: options) {
(didAllow, error) in
if !didAllow {
print("User has declined notifications")
}
}
notificationCenter.getNotificationSettings { (settings) in
if settings.authorizationStatus != .authorized {
print("Notifications not allowed")
}
}
let randomArrayNotificationQuote = Int(arc4random_uniform(UInt32(myQuotes.count)))
let randomArrayNotificationTitle = Int(arc4random_uniform(UInt32(myTitle.count)))
let content = UNMutableNotificationContent()
content.title = "\(myTitle[randomArrayNotificationTitle])"
content.body = "\(myQuotes[randomArrayNotificationQuote])"
content.sound = UNNotificationSound.default
content.categoryIdentifier = "com.giovannifilippini.philo"
// Deliver the notification
var dateComponents = DateComponents()
dateComponents.hour = 9
dateComponents.minute = 00
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let uuid = UUID().uuidString
let request = UNNotificationRequest.init(identifier: uuid, content: content, trigger: trigger)
notificationCenter.add(request) { (error) in
if error != nil {
print("add NotificationRequest succeeded!")
notificationCenter.removePendingNotificationRequests(withIdentifiers: [uuid])
}
}
The issue here is the trigger has the repeats set to true
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
You would need to set it to false and re run this method every time you want to schedule different notification (different content)
In order to receive the Notification while the app is in the foreground you need to implement the willPresent method from the UNUserNotificationCenterDelegate
Here is a simple example:
class ViewController: UIViewController {
private let userNotificaionCenter = UNUserNotificationCenter.current()
override func viewDidLoad() {
super.viewDidLoad()
// 1. Assign the delegate
userNotificaionCenter.delegate = self
scheduleNotification()
}
func scheduleNotification() {
// Same notification logic that you have
}
}
// 2. Implement the UNUserNotificationCenterDelegate
extension ViewController: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// This will allow the app to receive alerts and sound while in the foreground
completionHandler([.alert, .sound])
}
}
I'm learning the new notification system but I'm having trouble with actions. The notification works, but I do't get an action with it. I'm following a tutorial on https://www.appcoda.com/ios10-user-notifications-guide/ but I don't seem to get actions to appear with notification.
As you can see the commented out part in func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil)is where I cannot seem to get where it goes, can anyone see where I'm mistaking?
import UIKit
import UserNotifications
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func scheduleNotification(at date: Date) {
UNUserNotificationCenter.current().delegate = self
//compone a new Date components because components directly from Date don't work
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute)
//create the trigger with above new date components, with no repetitions
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
//create the content for the notification
let content = UNMutableNotificationContent()
content.title = "Tutorial Reminder"
content.body = "Just a reminder to read your tutorial over at appcoda.com!"
content.sound = UNNotificationSound.default()
// notification action category
//add an image to notification
//convert logo to url
if let path = Bundle.main.path(forResource: "logo", ofType: "png") {
let url = URL(fileURLWithPath: path)
do { // because UNNotificationAttachment is mark as throwing we need an attach block for handling errors
let attachment = try UNNotificationAttachment(identifier: "logo", url: url, options: nil)
content.attachments = [attachment]
content.categoryIdentifier = "myCategory"
} catch {
print("The attachment was not loaded.")
}
}
//create the request for notification with desired parameters
let request = UNNotificationRequest(identifier: "textNotification", content: content, trigger: trigger)
//add the request to notification center after removing all notifications
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
// let action = UNNotificationAction(identifier: "remindLater", title: "Remind me later", options: [])
// let category = UNNotificationCategory(identifier: "myCategory", actions: [action], intentIdentifiers: [], options: [])
// UNUserNotificationCenter.current().setNotificationCategories([category])
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) {(accepted, error) in
if !accepted {
// print("Notification access denied.")
// let action = UNNotificationAction(identifier: "remindLater", title: "Remind me later", options: [])
// let category = UNNotificationCategory(identifier: "myCategory", actions: [action], intentIdentifiers: [], options: [])
// UNUserNotificationCenter.current().setNotificationCategories([category])
}
}
let action = UNNotificationAction(identifier: "remindLater", title: "Remind me later", options: [])
let category = UNNotificationCategory(identifier: "myCategory", actions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
return true
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == "remindLater" {
let newDate = Date(timeInterval: 5, since: Date())
scheduleNotification(at: newDate)
}
// UNUserNotificationCenter.current().delegate = self
}
}
EDIT:
after #VIP-DEV answer I changed the code, to add the reminder in it's own action, into:
import UIKit
import UserNotifications
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
configureCategory()
triggerNotification(at: Date())
requestAuth()
return true
}
private let category = "Notification.Category.Read"
private let readActionIdentifier = "Read"
private let waitActionIdentifier = "Wait"
private func requestAuth() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (success, error) in
if let error = error {
print("Request Authorization Failed (\(error), \(error.localizedDescription))")
}
}
}
func triggerNotification(at date: Date) {
// Create Notification Content
let notificationContent = UNMutableNotificationContent()
//compone a new Date components because components directly from Date don't work
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute)
//create the trigger with above new date components, with no repetitions
// Configure Notification Content
notificationContent.title = "Hello"
notificationContent.body = "Kindly read this message."
// Set Category Identifier
notificationContent.categoryIdentifier = category
// Add Trigger
// let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 10.0, repeats: false)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
// // Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "test_local_notification", content: notificationContent, trigger: trigger)
//
// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
if let error = error {
print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
}
}
}
private func configureCategory() {
// Define Actions
let read = UNNotificationAction(identifier: readActionIdentifier, title: "Read", options: [])
let wait = UNNotificationAction(identifier: waitActionIdentifier, title : "Wait", options: [])
// Define Category
let readCategory = UNNotificationCategory(identifier: category, actions: [read, wait], intentIdentifiers: [], options: [])
// Register Category
UNUserNotificationCenter.current().setNotificationCategories([readCategory])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case readActionIdentifier:
print("Read tapped")
case waitActionIdentifier:
let NewDate = Date(timeInterval: 20, since: Date())
triggerNotification(at: NewDate)
print("Wait tapped")
default:
print("Other Action")
}
completionHandler()
}
}
and
class ViewController: UIViewController {
#IBAction func datePickerDidSelectNewDate(_ sender: UIDatePicker) {
let selectedDate = sender.date
let delegate = UIApplication.shared.delegate as? AppDelegate
// delegate?.scheduleNotification(at: selectedDate)
delegate?.triggerNotification(at: selectedDate)
}
}
Now I don't get to set new notification when tap on wait button. where do I have to set let NewDate = Date(timeInterval: 20, since: Date())
triggerNotification(at: NewDate)? I thought it went in case waitActionIdentifier but I don't even get the print to console.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UNUserNotificationCenter.current().delegate = self
configureCategory()
triggerNotification()
requestAuth()
return true
}
private let category = "Notification.Category.Read"
private let actionIdentifier = "Read"
private func requestAuth() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (success, error) in
if let error = error {
print("Request Authorization Failed (\(error), \(error.localizedDescription))")
}
}
}
private func triggerNotification() {
// Create Notification Content
let notificationContent = UNMutableNotificationContent()
// Configure Notification Content
notificationContent.title = "Hello"
notificationContent.body = "Kindly read this message."
// Set Category Identifier
notificationContent.categoryIdentifier = category
// Add Trigger
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 3.0, repeats: false)
// Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "test_local_notification", content: notificationContent, trigger: notificationTrigger)
// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
if let error = error {
print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
}
}
}
private func configureCategory() {
// Define Actions
let read = UNNotificationAction(identifier: actionIdentifier, title: "Read", options: [])
// Define Category
let readCategory = UNNotificationCategory(identifier: category, actions: [read], intentIdentifiers: [], options: [])
// Register Category
UNUserNotificationCenter.current().setNotificationCategories([readCategory])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case actionIdentifier:
print("Read tapped")
default:
print("Other Action")
}
completionHandler()
}
The declaration of the category identifier was declared as private.
Now the Notification actions work as expected.
Thanks to VIP-DEV's answer I could find how to correct my code.
I'm implemented push notification in my app in this way
//MARK:- Register notifications
func registerForPushNotifications() {
if #available(iOS 10.0, *){
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
if (granted)
{
UIApplication.shared.registerForRemoteNotifications()
}
else{
//Do stuff if unsuccessful...
}
// Enable or disable features based on authorization.
}
}
else
{
//If user is not on iOS 10 use the old methods we've been using
let types: UIUserNotificationType = [UIUserNotificationType.badge, UIUserNotificationType.alert, UIUserNotificationType.sound]
let settings: UIUserNotificationSettings = UIUserNotificationSettings( types: types, categories: nil )
UIApplication.shared.registerUserNotificationSettings( settings )
UIApplication.shared.registerForRemoteNotifications()
}
}
//MARK: Push Notifications Delegate Methods
func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) {
var token = ""
for i in 0..<deviceToken.count {
//token += String(format: "%02.2hhx", arguments: [chars[i]])
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
USER_DEFAULTS.setValue(token, forKey: "Device_ID")
USER_DEFAULTS.synchronize()
}
func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error ) {
print( error.localizedDescription )
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
UIApplication.shared.applicationIconBadgeNumber = 0
alertRemoteNotification(userInfo as NSDictionary)
}
//Code for showing alert when in foreground
func alertRemoteNotification(_ userInfo : NSDictionary)
{
if UIApplication.shared.applicationState == .active {
if let aps = userInfo as? NSDictionary {
if let apsDidt = aps.value(forKey: "aps") as? NSDictionary {
if let alertDict = apsDidt.value(forKey: "alert") as? NSDictionary {
if let notification_type = alertDict.value(forKey: "name") as? String {
if let notification_Message = alertDict.value(forKey: "body") as? String {
let alert = UIAlertController(title: notification_type.capitalized + " Alert", message: notification_Message, preferredStyle: UIAlertControllerStyle.alert)
let okayBtn = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
// When Okay
UIApplication.shared.applicationIconBadgeNumber = 0
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications() // To remove all delivered notifications
center.removeAllPendingNotificationRequests()
} else {
// Fallback on earlier versions
UIApplication.shared.cancelAllLocalNotifications()
}
let rootViewController = self.window!.rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let dashBoardVC = mainStoryboard.instantiateViewController(withIdentifier: "DashBoardVC") as! DashBoardVC
rootViewController.pushViewController(dashBoardVC, animated: false)
})
let cancelBtn = UIAlertAction(title: "Cancel", style: .default, handler: { (action) -> Void in
UIApplication.shared.applicationIconBadgeNumber = 0
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications() // To remove all delivered notifications
center.removeAllPendingNotificationRequests()
} else {
// Fallback on earlier versions
UIApplication.shared.cancelAllLocalNotifications()
}
})
alert.addAction(okayBtn)
alert.addAction(cancelBtn)
self.window?.rootViewController!.present(alert, animated: true, completion: nil)
}
}
}
}
}
}
else {
let rootViewController = self.window!.rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let dashBoardVC = mainStoryboard.instantiateViewController(withIdentifier: "DashBoardVC") as! DashBoardVC
rootViewController.pushViewController(dashBoardVC, animated: false)
}
}
//Delegate methods
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.sound, .alert, .badge])
UIApplication.shared.applicationIconBadgeNumber = 0
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo as NSDictionary
completionHandler()
self.alertRemoteNotification(userInfo as NSDictionary)
}
I could able to access the responce after tapping notification banner but the actual issue is when i'm in foreground i need to show an alert with the notification responce without tapping on notification banner. Please let me know how can get responce without tapping on notification banner.
iOS 10+ Provides delegate userNotificationCenter:willPresentNotification:withCompletionHandler
Asks the delegate how to handle a notification that arrived while the
app was running in the foreground.
And this will call only if app opened.
Also you can use CONTENT-AVAILABLE=1 for triggering methods.
FLOW:(Without Taping Notification, content-available:1)
App Opened State:- willPresentNotification(ios10+) -> didReceiveRemoteNotification:fetchCompletionHandler
App in Background:- didReceiveRemoteNotification:fetchCompletionHandler
App Closed:- You won't get notification data unless, the app opened by clicking Notification
Alternate method: Using Rich Notification
You can use Notification Extensions to create custom push notifications(contents including images/videos). Notification Service Extension & Notification Content Extension used to achieve this. mutable-content:1 required to trigger this. Here you can download images, get data, etc. [But the data can be shared with App ONLY through UserDefaults(App Groups), correct me if i'm wrong]
You could search for some random tutorials
Create notification service extension to process notification data. you will get 30seconds to process pushnotification via notification service extension
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
if let copy = request.content.mutableCopy() as? UNMutableNotificationContent {
// Process your notification here
contentHandler(copy)
}
}
Hope this will help you
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.sound, .alert, .badge])
UIApplication.shared.applicationIconBadgeNumber = 0
// Added This line
alertRemoteNotification(notification.request.content.userInfo as NSDictionary)
}
Working without any issues.
My code uses a datePicker, that when the datePicker is changed and matches the current date of the user device. It fires a notification. However the AVSpeechUtterance I am using fires as soon as the datePickers time is changed not when the notification appears. I would like the AVSpeechUtterance and notification to be be fired at the same time.
import UIKit
import AVFoundation
import UserNotifications
class ViewController: UIViewController {
#IBOutlet var datePicker: UIDatePicker!
#IBAction func datePicker(_ sender: Any) {
let c = UNMutableNotificationContent()
c.title = "Lets Roll"
c.subtitle = "s"
c.body = "d"
let begin = AVSpeechUtterance(string: " Hello ")
let synthesizer = AVSpeechSynthesizer()
begin.voice = AVSpeechSynthesisVoice(language: "en-US")
begin.rate = 0.08
synthesizer.speak(begin)
let triggerDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: datePicker.date )
let t = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
let r = UNNotificationRequest(identifier: "any", content: c, trigger: t)
UNUserNotificationCenter.current().add(r, withCompletionHandler: nil)
}}
APP DELEAGATE
import AVFoundation
import UIKit
import UserNotifications
enum NotificationName: String {
case mySpeechNotification
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if error != nil {
print("Ops, error trying to get authorization")
} else {
if !granted {
print("Dude, let me use notifications!")
}
}
}
}
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Oh, will present a notification, let's see the identifier: \(notification.request.identifier)")
if (notification.request.identifier == NotificationName.mySpeechNotification.rawValue) {
print("Speaking...")
} else {
print("Nothing to say...")
}
completionHandler(.alert)
let begin = AVSpeechUtterance(string: " Hello ")
begin.voice = AVSpeechSynthesisVoice(language: "en-US")
begin.rate = 0.08
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(begin)
}
}
You can perform your speech action when the notification is received. To achieve this you can:
If you're below iOS 10 you can use application:didReceiveLocalNotification: on UIAppplicatinoDelegate and then start your speech thing.
An idea of implementation would be to check some value on the notification to make sure that is the "speech notification" you triggered.
If you're on iOS 10+ you can use the UNUserNotificationCenterDelegateExample here you can take a look of an example implementation.
Have you tried with setting preUtteranceDelay for AVSpeechUtterance ?
import UIKit
import UserNotifications
import UserNotificationsUI //framework to customize the notification
class TestViewController: UIViewController {
#IBOutlet weak var datePicker: UIDatePicker!
let appDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
datePicker.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
}
//MARK:- Date Picker Value Changed
func dateChanged(_ sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd, yyyy"
let selectedDate = dateFormatter.string(from: datePicker.date)
let currentDate = dateFormatter.string(from: Date())
if selectedDate == currentDate{
self.triggerLocalNotification()
}
print(selectedDate)
}
//MARK:- Removed all pending notifications
func removeLocalNotification() {
print("Removed all pending notifications")
let center = UNUserNotificationCenter.current()
center.removePendingNotificationRequests(withIdentifiers: [appDelegate.requestIdentifier])
}
//MARK:- Trigger Local Notification
func triggerLocalNotification() {
self.removeLocalNotification()
let content = UNMutableNotificationContent()
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "Body"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5.0, repeats: false)
let request = UNNotificationRequest(identifier: appDelegate.requestIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request){(error) in
if (error != nil){
print(error?.localizedDescription)
}
}
}
}
//============AppDelegate=========
import UIKit
import AVFoundation
import UserNotifications
let center = UNUserNotificationCenter.current()
let requestIdentifier = "SampleRequest" //identifier is to cancel the notification request
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Requesting Authorization for User Interactions
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
center.delegate = self
return true
}
extension AppDelegate: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")
let begin = AVSpeechUtterance(string: " Hello ")
begin.voice = AVSpeechSynthesisVoice(language: "en-US")
begin.rate = 0.08
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(begin)
//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])
}
}
}
============
I have created a scheduled notification in ViewController.swift and need two actions to be added to this notification. One that says "Call", and the other that says "Cancel". How can I add these actions in ViewController.swift? Here is the part of the code that has the function for firing my notification in my ViewController.swift:
func notificationFires(){
let notification = UILocalNotification()
// 2
notification.soundName = UILocalNotificationDefaultSoundName
notification.fireDate = datePicker.date
// 3
if textField.text == "" {
notification.alertBody = "You have a call right now!"
}
else{
notification.alertBody = self.textField.text
}
// 4
notification.timeZone = NSTimeZone.default
// 5
// 6
notification.applicationIconBadgeNumber = 1
// 7
UIApplication.shared.scheduleLocalNotification(notification)
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("Recived: notification")
if cancelled == true{
print("cancelled happened")
}
func cancelNotify(){
cancelled = true
UIApplication.shared.cancelAllLocalNotifications()
}
completionHandler(.newData)
}
}
I haven't used notifications in iOS 10 yet, so I went ahead and figured it out as a learning experience for myself, now I can pass it on to you.
UILocalNotification is depreciated in iOS 10 and replaced by the UserNotifications framework.
In your AppDelegate get authorization from the user to show notifications and set up the centers delegate with UNUserNotificationCenterDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let center = UNUserNotificationCenter.current()
center.delegate = self
let options: UNAuthorizationOptions = [.alert, .sound];
center.requestAuthorization(options: options) {
(granted, error) in
if !granted {
print("Something went wrong")
}
}
return true
}
Present the notification to the user:
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Play sound and show alert to the user
completionHandler([.alert,.sound])
}
Handling the actions:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// Determine the user action
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
print("Dismiss Action")
case UNNotificationDefaultActionIdentifier:
print("Default")
case "foo":
print("foo")
case "bar":
print("bar")
default:
print("Unknown action")
}
completionHandler()
}
Do this wherever you want to setup all actions and categories for all notifications in your app. Because they're assigned to the center, not the notification itself:
func setupActions() {
//create first action
let foo = UNNotificationAction(
identifier: "foo",
title: "foo"
)
//create second action
let bar = UNNotificationAction(
identifier: "bar",
title: "bar",
options: [.destructive]
)
//put the two actions into a category and give it an identifier
let cat = UNNotificationCategory(
identifier: "cat",
actions: [foo, bar],
intentIdentifiers: []
)
//add the category to the notification center
UNUserNotificationCenter.current().setNotificationCategories([cat])
}
And finally creating the actual notification:
func setupNotification() {
let content = UNMutableNotificationContent()
content.title = "Hello!"
content.body = "A message"
content.sound = UNNotificationSound.default()
//make sure to assign the correct category identifier
content.categoryIdentifier = "cat"
// Deliver the notification in five seconds.
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "hello", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
if let theError = error {
print("theError \(theError)")
}
}
}
Be sure to import UserNotifications in each class utilizing these functions.
More info: User Notifications documentation.