Reputation: 437
The local notification is supposed to fire at 25-9-2021. Here is the print object of fire time
▿ year: 2021 month: 9 day: 26 isLeapMonth: false
- year : 2021
- month : 9
- day : 25
- isLeapMonth : false
let content = UNMutableNotificationContent()
content.title = "Test title"
content.body = "sample test body"
var trigger:UNCalendarNotificationTrigger
let n = 1
let nextTriggerDate = Calendar.current.date(byAdding: .day, value: n, to: Date())!
let comps = Calendar.current.dateComponents([.year, .month, .day], from: nextTriggerDate)
trigger = UNCalendarNotificationTrigger(dateMatching: comps, repeats: false)
content.subtitle = "Sub title-Date-NonRepeat"
let request = UNNotificationRequest(identifier: "test.my.example", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { Error in
if let err = Error {
print("Notification Error:\(String(describing: err))")
}
}
Again I added time with Date with below code changes
var comps = Calendar.current.dateComponents([.year, .month, .day], from: nextTriggerDate)
comps.day = 25
comps.hour = 12
comps.minute = 17
comps.second = 10
Here is PO of comps variable
var comps = Calendar.current.dateComponents([.year, .month, .day], from: nextTriggerDate) comps.day = 25 comps.hour = 12 comps.minute = 17 comps.second = 10
I have given just date to see if it fires and I have given just date and time to see if it fires and I'm doing the whole thing in main thread and it's not working
Can someone make me understand what am I doing wrong here
Upvotes: 1
Views: 1443
Reputation: 15
I have gone through your code , It's very clear you are adding calendar trigger properly and you have mentioned in one of the comments that you have got necessary permission as well while adding.
To me it feels like some datetime issue. Check if the datetime to datetime components converstion takes place properly.
If you are testing on simulator things can get crazy when you change the dates in the system back and forth.
I'd suggest you to test with real device.
This is how you get permission
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.announcement,.sound]) { [self]
(granted, error) in
if granted {
print("granted")
} else {
print("denied or error")
}
}
You can query list of available notifications using below code just to if it's scheduled properly at the expected datetime.
var text:NSMutableAttributedString? = NSMutableAttributedString(string: "List of notification requests and it's time\n")
UNUserNotificationCenter.current().getPendingNotificationRequests() { [weak self] requests in
DispatchQueue.main.async {
for request in requests {
guard let trigger = request.trigger as? UNCalendarNotificationTrigger else { return }
text?.append(NSAttributedString(string: "\nTrigger Date:\(trigger.nextTriggerDate()?.description) \nDateTime Component:\(trigger.dateComponents.description)\n"))
}
print(text)
}
func getFireDatetime(apiDate: Date, numberOfDaysBeforeApiDate: Int,repeat:Bool) -> DateComponents? {
if let fireDate: Date = apiDate.dateByAdding(days: -numberOfDaysBeforeApiDate) {
if repeat {
return Calendar.current.dateComponents([.day, .hour, .minute, .second], from: fireDate)
}
return Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: fireDate)
}
return nil
}
Invoke getFireDatetime method and that should fix all your problem I guess
P.S
Date() it gives you time back in universal time whereas Datetime components will give the Datetime with current calendar settings of your device.
As you are using Datetime components Timezone change and day light savings should be handled automatically
Upvotes: 0
Reputation: 29261
You likely aren't requesting permission first
import SwiftUI
//struct and class should start with an uppercase
struct NotificationView: View {
//Central location for Notification code including the delegate
// A call to the notificationManager just like the one below has to be included in
// application(_:willFinishLaunchingWithOptions:) or
// application(_:didFinishLaunchingWithOptions:)
//https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate
//https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-an-appdelegate-to-a-swiftui-app
let notificationManager: NotificationManager = NotificationManager.shared
var body: some View {
VStack {
VStack {
Button("Request Permission") {
//Call a func here don't define it
notificationManager.requestAuthorization()
}
.frame(width: 200, height: 60, alignment: .center)
.foregroundColor(.black)
.background(Color.blue)
.cornerRadius(10.0)
.padding()
Button("Add custom trigger") {
let apiDate = DateComponents(year: 2021, month: 11, day: 10, hour: 16, minute: 0, second: 0)
notificationManager.scheduleBasedOnDaysBeforeDate(title: "test", body: "test", baseDate: Calendar.current.date(from: apiDate)!, xDaysBefore: 10, count: 12, identifier: UUID().uuidString)
}
.padding()
Button("Print Notifications") {
//Reusable method
self.notificationManager.printNotifications()
}
Button("Print Delivered Notifications") {
//Reusable method
self.notificationManager.printDeliveredNotifications()
}
.foregroundColor(.blue)
.padding()
Button("Delete Notifications") {
//Reusable method
self.notificationManager.deleteNotifications()
}
.foregroundColor(.blue)
.padding()
}
}
}
}
class NotificationManager: NSObject, UNUserNotificationCenterDelegate{
//Singleton is requierd because of delegate
static let shared: NotificationManager = NotificationManager()
let notificationCenter = UNUserNotificationCenter.current()
private override init(){
super.init()
//This assigns the delegate
notificationCenter.delegate = self
}
func scheduleUNCalendarNotificationTrigger(title: String, body: String, dateComponents: DateComponents, identifier: String, repeats: Bool = false){
print(#function)
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: repeats)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
notificationCenter.add(request) { (error) in
if error != nil {
print(error!)
}
}
}
func scheduleUNTimeIntervalNotificationTrigger(title: String, body: String, timeInterval: TimeInterval, identifier: String, repeats: Bool = false){
print(#function)
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: repeats)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
notificationCenter.add(request) { (error) in
if error != nil {
print(error!)
}
}
}
///Schedules `count` number of monthly notifications that occur `xDaysBefore` the `baseDate`
func scheduleBasedOnDaysBeforeDate(title: String, body: String, baseDate: Date, xDaysBefore: Int, count: Int, identifier: String){
print(#function)
var nextBaseDate: Date = baseDate
for n in 1...count{
guard let triggerDate: Date = Calendar.current.date(byAdding: .day, value: -xDaysBefore, to: nextBaseDate) else{
return
}
let components: DateComponents = Calendar.current.dateComponents([.month,.day, .hour,.minute,.second], from: triggerDate)
let id = identifier.appending(" \(n)")
scheduleUNCalendarNotificationTrigger(title: title, body: body, dateComponents: components, identifier: id)
//OR if you want specific seconds
//let interval = Calendar.current.dateComponents([.second], from: Date(), to: triggerDate).second ?? 1
//scheduleUNTimeIntervalNotificationTrigger(title: title, body: body, timeInterval: TimeInterval(interval), identifier: id)
let next = Calendar.current.date(byAdding: .month, value: 1, to: nextBaseDate)
if next != nil{
nextBaseDate = next!
}else{
print("next == nil")
return
}
}
self.printNotifications()
}
func requestAuthorization() {
print(#function)
notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Access Granted!")
} else {
print("Access Not Granted")
}
}
}
func deleteNotifications(){
print(#function)
notificationCenter.removeAllPendingNotificationRequests()
}
///Prints to console schduled notifications
func printNotifications(){
print(#function)
notificationCenter.getPendingNotificationRequests { request in
print("UNTimeIntervalNotificationTrigger Pending Notification")
for req in request{
if req.trigger is UNTimeIntervalNotificationTrigger{
print((req.trigger as! UNTimeIntervalNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
}
}
print("UNCalendarNotificationTrigger Pending Notification")
for req in request{
if req.trigger is UNCalendarNotificationTrigger{
print((req.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
}
}
}
}
///Prints to console delivered notifications
func printDeliveredNotifications(){
print(#function)
notificationCenter.getDeliveredNotifications { request in
for req in request{
print(req)
}
}
}
//MARK: UNUserNotificationCenterDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.banner)
}
}
struct NotificationView_Previews: PreviewProvider {
static var previews: some View {
NotificationView()
}
}
Run this code. Click on "Request Permission" first then click on "Add custom trigger" you should see your notification in the console when you click on "print"
Upvotes: 2