Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to update local scheduled notification content

In one of the WWDC sessions I got code snippet for updating existing notifications. I don't think it works. Trying to update notification content.

First I request pending notifications from UNUserNotificationCenter which always works. Then I am creating new request to update notification with existing unique identifier.

There's 1 new variable content: String.

// Got at least one pending notification.
let triggerCopy = request!.trigger as! UNTimeIntervalNotificationTrigger
let interval = triggerCopy.timeInterval
let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: true)

// Update notificaion conent.
let notificationContent = UNMutableNotificationContent()
notificationContent.title = NSString.localizedUserNotificationString(forKey: "Existing Title", arguments: nil)
notificationContent.body = content
let updateRequest = UNNotificationRequest(identifier: request!.identifier, content: notificationContent, trigger: newTrigger)
UNUserNotificationCenter.current().add(updateRequest, withCompletionHandler: { (error) in
    if error != nil {
        print("🚫 Couldn't update notification \(error!.localizedDescription)")
    }
})

I am unable to catch error. The problem is that notification content body doesn't change.

Update.

I also tried to change trigger with different repeat interval. It doesn't work, notification is repeated with the same original interval it was created with.

Update 2.

Read Chris' answer, trying to go with first option.

let center = UNUserNotificationCenter.current()
center.getPendingNotificationRequests(completionHandler: { (requests) in
    for request in requests {
        if request.identifier == notificationIdentifier {
            // Got at least one pending notification,
            // update its content.
            let notificationContent = UNMutableNotificationContent()
            notificationContent.title = NSString.localizedUserNotificationString(forKey: "new title", arguments: nil)
            notificationContent.body = "new body"
            request.content = notificationContent // ⛔️ request.content is read only.
        }
    }
})

As you can see I can't modify original request.

Update 3.

Had go with second "delete first" option. Noticed that calling removePendingNotificationRequests and schedule after, still gives me old notification version. I had to add 1 second delay between calling removePendingNotificationRequests and center.add(request).

Marked Chris' answer as accepted but feel free to share better option.

like image 476
Boris Y. Avatar asked Jul 11 '17 11:07

Boris Y.


2 Answers

The problem is that you're not modifying the existing notification, and instead adding a new notification with a duplicate identifier.

Let's tackle the duplicate issue first, the reason this duplicate notification doesn't show up is because the identifier isn't unique. From the docs:

(if identifier is not unique, notifications are not delivered).

You have two options. You can 1) modify the existing Notification, or 2) remove it and add the new one.

For 1, you already have the request, instead of pulling the trigger and identifier out of it, just replace request.content with your updated notificationContent.

For 2, you would just need to add a line before your Add:

UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [request!.identifier])
like image 177
Chris Allwein Avatar answered Nov 05 '22 07:11

Chris Allwein


After I've requested to Allow Notifications:

I trigger a notification right from my viewDidLoad but then also trigger another one with the same identifier. At the end the the updatedBody/updatedTitle show up.

import UIKit
import UserNotifications

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let content = UNMutableNotificationContent()
        content.title = "Scheduled Task"
        content.body = "dumbBody"
        content.badge = 1
        content.sound = UNNotificationSound.default()
        content.categoryIdentifier = "alertCategory"

        UNUserNotificationCenter.current().delegate = self

        //Setting time for notification trigger
        let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 3.0, repeats: false)
        let request = UNNotificationRequest(identifier:"myIdentifier", content: content, trigger: trigger)
        UNUserNotificationCenter.current().add(request, withCompletionHandler: {_ in print(" was registered")})

        updateNotification()
    }

My update function

    func updateNotification(){

        let center = UNUserNotificationCenter.current()
        var request : UNNotificationRequest?

        center.getPendingNotificationRequests{ notifications in
            for notificationRequest in notifications{
                if notificationRequest.identifier == "myIdentifier"{
                    request = notificationRequest
                    center.removeAllPendingNotificationRequests() // Removing this line or keeping it makes NO difference
                }

            }

            let newTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5.0, repeats: false)

            // Update notificaion conent.
            let notificationContent = UNMutableNotificationContent()
            notificationContent.title = "UpdatedTitle"

            notificationContent.body = "updatedBody"
            let updateRequest = UNNotificationRequest(identifier: request!.identifier, content: notificationContent, trigger: newTrigger)
            UNUserNotificationCenter.current().add(updateRequest, withCompletionHandler: { (error) in
                print("successfully updated")
                if error != nil {
                    print("🚫 Couldn't update notification \(error!.localizedDescription)")
                }
            })
        }

    }

}

In the above snippet: Removing center.removeAllPendingNotificationRequests() would make no difference. Still I would receive the updatedNotification.

For handling incoming notifications

extension ViewController:UNUserNotificationCenterDelegate{      

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    print("original identifier was : \(response.notification.request.identifier)")
    print("original body was : \(response.notification.request.content.body)")
    print("Tapped in notification")

    switch response.actionIdentifier {
    default:
        print("some action was clicked")
    }
}

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

        print("Notification being triggered")
        completionHandler( [.alert,.sound,.badge])

    }
}
like image 2
mfaani Avatar answered Nov 05 '22 08:11

mfaani