nextTriggerDate() doesn't return the 'expected' value, is there another way to obtain the next fire date of a repeating Local Notification?
Confirmed. I ran this code:
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120, repeats: true)
print("scheduling at", Date())
DispatchQueue.main.asyncAfter(deadline: .now()+15) {
print("checking at", Date())
UNUserNotificationCenter.current().getPendingNotificationRequests {
arr in let arr = arr
if let req = arr[0].trigger as? UNTimeIntervalNotificationTrigger {
let fd = req.nextTriggerDate()
print("trigger date", fd as Any)
}
}
}
// ... proceed to configure and schedule the notification
I also configured my user notification center delegate to receive the notification in the foreground and print the time.
Here's what I saw in the console:
scheduling at 2018-08-01 03:40:36 +0000
checking at 2018-08-01 03:40:51 +0000
trigger date Optional(2018-08-01 03:42:51 +0000)
received notification while active 2018-08-01 03:42:36 +0000
So the trigger date was reported as 2 minutes from when I checked, but the notification actually fired 2 minutes from when I scheduled it.
I'd describe that as a bug!
The one disagreement I'd have with your original question is that I get exactly the same result for a non-repeating UNTimeIntervalNotificationTrigger:
scheduling at 2018-08-01 03:45:50 +0000
checking at 2018-08-01 03:46:06 +0000
trigger date Optional(2018-08-01 03:48:06 +0000)
received notification while active 2018-08-01 03:47:50 +0000
A UNTimeIntervalNotificationTrigger also has a timeInterval
property, but even that does us no good unless we know when the notification was originally scheduled — and a UNNotificationRequest provides no way to find that out.
(Just to be certain, I postponed my checking until the repeating notification had fired a couple of times, but the result was the same: nextTriggerDate
is clearly adding the timeInterval
to now rather than reporting the time at which the notification will next fire.)
How to get date of upcoming notification in swift?
You want to use dateComponents
of the UNCalendarNotificationTrigger
. try using notification.fireDate
to retrieve that.
More info on Apple Developer Documentation: https://developer.apple.com/documentation/usernotifications/uncalendarnotificationtrigger
Accessing the scheduled date of a UNNotificationRequest object
can't find a way to access the scheduled fire date of the notification.
You have already shown that you understand how to get the UNNotificationTrigger. Well, UNNotificationTrigger is an abstract superclass. You need to find out what class it really is and cast it down to that class. Then you can explore its properties.
For example:
If it is a UNCalendarNotificationTrigger, then cast it down to a UNCalendarNotificationTrigger. Now it has a
nextTriggerDate
.If it is a UNTimeIntervalNotificationTrigger, then cast it down to a UNTimeIntervalNotificationTrigger. Now it has a
nextTriggerDate
.
Edit But note that there is a massive bug: if this is a UNTimeIntervalNotificationTrigger, the nextTriggerDate
will be wrong.
Why does PHP 8 return the wrong second Friday of the next Year
It is not a bug. “Second Friday in January next year” is interpreted as “second Friday in January” of this year and “next year” as +1 year. With long expressions it is not always clear in which order they are processed. It is usually better to do this in several steps.
$jan2 = date_create('next year')->modify('second friday of january'); //2022-01-14
UNCalendarNotificationTrigger not initiating
After getting more details, I guess I know why you still don't see the notifications being delivered. I'm making it in another answer to not have it too long, but I'll keep my previous answer for reference.
Maybe you were waiting for the notification with the application in foreground? I'll refer to another part of the documentation:
Scheduling and Handling Local Notifications
On the section about Handling Notifications When Your App Is in the Foreground:
If a notification arrives while your app is in the foreground, you can
silence that notification or tell the system to continue to display
the notification interface. The system silences notifications for
foreground apps by default, delivering the notification’s data
directly to your app...
So, if that's the case, you must implement a delegate for UNUserNotificationCenter
.
I suggest you something like this, where on AppDelegate you assign the delegate for UNUserNotificationCenter
since documentation says it must be done before application finishes launching:
// AppDelegate.swift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
// Rest of your code on AppDelegate...
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Here we actually handle the notification
print("Notification received with identifier \(notification.request.identifier)")
// So we call the completionHandler telling that the notification should display a banner and play the notification sound - this will happen while the app is in foreground
completionHandler([.banner, .sound])
}
}
On the view controller you have handling the notification authorization and request registration, you could do it like this:
class NotificationsViewController: UIViewController {
static let notificationAuthorizedNotification = NSNotification.Name(rawValue: "NotificationAuthorizedNotification")
let randVerseCenter = UNUserNotificationCenter.current()
override func viewDidLoad() {
super.viewDidLoad()
// We call this method when we know that the user granted permission, so we know we can then make notification requests
NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationAuthorization), name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
randVerseCenter.getNotificationSettings { [weak self] settings in
// We check current settings and asks for permission if not granted before
if settings.authorizationStatus == .notDetermined {
// Step 1 - Ask the use for permission to notify
self?.randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
if granted {
NotificationCenter.default.post(name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
print("Yay - request authorisation worked!")
} else {
print ("D'oH - request Authorisation did not work!")
}
}
}
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// We stop listening to those notifications here
NotificationCenter.default.removeObserver(self)
}
@objc
func handleNotificationAuthorization() {
// Step 2 - Create the Notification Content
let randVerseContent = UNMutableNotificationContent()
randVerseContent.title = "Random Reference"
randVerseContent.body = "Random Verse"
randVerseContent.sound = UNNotificationSound.default
// Step 3 - Create the trigger for the notification by delay
let randVerseDate = Date().addingTimeInterval(30)
let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)
let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
// Step 4 - Creating the request
let randVerseUUIDString = UUID().uuidString
let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
// Step 5 - Register the request
randVerseCenter.add(randVerseRequest) { (error) in
if let error = error{
print (error.localizedDescription)
} else {
print("Successfully registered notification with id \(randVerseUUIDString) at every second \(randVerseDateComponents.second!) of a minute")
}
}
}
}
You might still have older notifications scheduled since your code was requesting them at the viewDidLoad
and maybe you didn't remove them or delete the app.
You can check the pending notifications using this on your viewDidLoad
for example:
randVerseCenter.getPendingNotificationRequests() { requests in
for request in requests {
guard let trigger = request.trigger as? UNCalendarNotificationTrigger else { return }
print("Notification registered with id \(request.identifier) is schedulled for \(trigger.nextTriggerDate()?.description ?? "(not schedulled)")")
}
}
And use randVerseCenter
to remove them by their identifiers or remove all of them.
Related Topics
How to Hash a String to Sha512 in Swift
Unsafemutablepointer<Void> to Concrete Object Type
How to Use a Value Type Object as a Reference Type
Swift Protocol for String Interpolation
How to Cast a Metaclass Object to a Protocol Type in Swift
How to Create a Multiline Textfield in Swiftui? Like the Notes App
Nspopover to Start in a Detached State
Safari App Extension Crashes After a Few Seconds for Hello World Project
Subscript of a Struct Doesn't Set Values When Created as an Implicitly Unwrapped Optional
How to Check If Airpods Are Connected to Iphone
Spritekit Particle Emitter Multi Image
Different Colors for Bars in Barchart Depend on Value
How to Get the Unicode Codepoint Represented by an Integer in Swift