1

I'm trying to reschedule a notification when user tap on wait notification action. When triggerNotification() gets called the first time Dateis provided by a Date Picker, but when it's called by a response gets the date from a time interval. Inside the didReceiveResponsemethod case:waitActionIdentifier: I set newDate and pass that to the triggerNotificationparameter. The delayed notification never arrives. I don't understand if it's a function calling problem or else because newDatei see is correct by the print of it. Here's the code:

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)
    }
}

and the AppDelegate

import UIKit
import UserNotifications

@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    var window: UIWindow?



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        UNUserNotificationCenter.current().delegate = self
        configureCategory()
        triggerNotification(at: Date())
        requestAuth()

        return true
    }


     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
        let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)

        // 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)

//        // 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 actualDate = Date()
            let newDate: Date = Date(timeInterval: 10, since: actualDate)
            self.triggerNotification(at: newDate)
            print("Wait tapped")
            print(actualDate)
            print(newDate)
        default:
            print("Other Action")
        }

        completionHandler()
    }


}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Vincenzo
  • 5,304
  • 5
  • 38
  • 96
  • FYI I tried your code. I was not able to even get the initial notification using your existing code. Though when I tried with your commented out `notificationTrigger` it was triggered for the first time and also I was able to repeat it. For some reason `UNCalendarNotificationTrigger` is buggy for me. I remember last time I used [this](https://stackoverflow.com/questions/40624120/uncalendarnotificationtrigger-doesnt-get-stored-unless-repeats-is-true) question and even doing that wasn't helpful. – mfaani Sep 04 '18 at 02:31
  • I also followed the approach from [here](https://www.hackingwithswift.com/example-code/system/how-to-set-local-alerts-using-unnotificationcenter) still wasn't able to set it. Not sure what I'm doing wrong. Ping me if you found a solution – mfaani Sep 04 '18 at 02:31
  • @Honey. Well.. for me using the commented out notificationTrigger didn't change anything as it shouldn't. No repetitions. Not sure why it's behaving as it does on your system. It's really weird as for what I know. That UNCalendarNotificationTrigger is not supposed to be used at all. Hence it's commented out. – Vincenzo Sep 04 '18 at 05:29
  • The logic here is : 1st notification is set in `triggerNotification()` with date from `Date picker`. 2nd Notification should be still from `triggerNotification()` with date from `case:wait` action response. Can you do a test? Set at 15 seconds the commented out `notificationTrigger`, and at 5 the `time difference` in `didReciveResponse`and see at what time you get the repetition. can you make a screenshot of the repetition please? Thank you Honey – Vincenzo Sep 04 '18 at 05:46
  • @Honey. One more thing, did you use a date picker? because it's very weird the way it behaves in your system, but I think I understand that if you didn't use a picker than of course the `trigger notification`sets a notification 10 seconde after you run the app.. – Vincenzo Sep 04 '18 at 05:50
  • NO, forget my previous comment, as if you don't use a datePicker than you won't call the `triggerNotification`method from `ViewController` – Vincenzo Sep 04 '18 at 05:59
  • I never used the code from the `Viewcontroller` class. I was just using the ` triggerNotification(at: Date())` from your AppDelegate. Doesn't that already do a `triggernotification` from your didFinish? – mfaani Sep 04 '18 at 14:18
  • @Honey. Nope, the first notification gets triggered from ViewController `datePickerDidSelectNewDate`.. that's why it was behaving strangely. Can you try using the Date picker and confirm if you get to reschedule a notification on tapping "wait" action button please? Thank you – Vincenzo Sep 04 '18 at 14:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/179389/discussion-between-honey-and-vincenzo). – mfaani Sep 04 '18 at 14:39

1 Answers1

0

After trying different thing with @VIP-DEV and @Honey I finally understood where the problem with rescheduling notification when tapping on the wait action button from the notification. Inside the func triggerNotification(at date: Date)scope I was getting the date components without seconds let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute)so when the new call of the function was made with a let newDate: Date = Date(timeInterval: 5.0 , since: actualDate)date, of course those seconds weren't taken into account and the date used was already passed, hence..no rescheduling . let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute, second: components.second) is the final date with components including seconds.

Vincenzo
  • 5,304
  • 5
  • 38
  • 96