In my iOS app, I want to show a local notification that appears in the notification center and in the lock screen. It aims to be a notification for a music player.
So far, here is what I tried, running on iOS 15.3.1:
at app startup:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (granted, error) in
if (granted)
{
let category = UNNotificationCategory(identifier: "streaming", actions: [], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
UNUserNotificationCenter.current().delegate = self
}
else
{
print("Notifications permission denied because: \(error?.localizedDescription).")
}
}
}
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
if #available(iOS 14, *)
{
completionHandler([.list])
}
else
{
completionHandler([.alert])
}
}
when I want to show the local notification:
// content:
let content = UNMutableNotificationContent()
content.categoryIdentifier = "test_category"
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "Notification body"
content.sound = UNNotificationSound.default
// trigger:
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
But for some reason, I only see the notification in the notification center, not in the lock screen...
So how can I see the notification in both the notification center and in the lock screen, as it is done for example on the Spotify app?
Thanks.
Related
I'm attempting to run a a simple iOS application that pushes a notification to a user's screen after a specified time.
So far, this is what I have (borrowed from another thread):
DispatchQueue.global(qos: .background).async {
print( "background task" )
DispatchQueue.main.asyncAfter( deadline: .now() + milliseconds( 2000 )) {
let content = UNMutableNotificationContent()
content.body = "Testing :)"
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger( timeInterval: 2, repeats: false )
let request = UNNotificationRequest( identifier: "test", content: content, trigger: trigger )
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
print( "background finish" )
}
}
My only issue is that the aSync After doesn't run whenever the app is in the background.
For example, if a user goes into their lockscreen or a different app, the notification never gets triggered.
Would anyone have a suggestion for how I could achieve this?
Thank you! :)
Approach:
Use UNNotificationRequest with time interval
Below mentioned solution would work in the following scenarios:
Foreground
Background
App is closed
Steps:
Set the delegate (to be alerted in foreground)
Request authorisation from user to be alerted
Create the notification
Add it to the notification center
AppDelegate:
AppDelegate must conform to UNUserNotificationCenterDelegate.
Set the notification center's delegate to the AppDelegate
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UNUserNotificationCenter.current().delegate = self
return true
}
//MARK: UNUserNotificationCenterDelegate
//This is required to be alerted when app is in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("will present")
completionHandler([.alert, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print("did receive")
}
}
Setting up notification:
import UserNotifications
private func setupNotification() {
requestAuthorization { [weak self] isGranted, error in
if let error = error {
print("Request Authorization Error: \(error)")
return
}
guard isGranted else {
print("Authorization Denied")
return
}
self?.addNotification()
}
}
private func requestAuthorization(completionBlock: #escaping (Bool, Error?) -> ()) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { isGranted, error in
completionBlock(isGranted, error)
}
}
private func addNotification() {
let content = UNMutableNotificationContent()
content.title = "Testing Notification"
content.body = "This is a test for notifications"
content.sound = .default()
let timeInterval = TimeInterval(5)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: "Something",
content: content,
trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { error in
if let error = error {
print("Error adding notification request: \(error)")
}
else {
print("Successfully added notification request")
}
}
}
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.
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)
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 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.