为啥本地通知未针对 UNCalendarNotificationTrigger 触发

Posted

技术标签:

【中文标题】为啥本地通知未针对 UNCalendarNotificationTrigger 触发【英文标题】:Why local notification is not firing for UNCalendarNotificationTrigger为什么本地通知未针对 UNCalendarNotificationTrigger 触发 【发布时间】:2021-11-17 11:21:25 【问题描述】:

本地通知应该在 25-9-2021 触发。这是火时间的打印对象

▿年:2021月:9日:26 isLeapMonth:假

年份:2021 月:9 天:25 isLeapMonth:假
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))")
    

我再次通过以下代码更改添加了 Date 时间

var comps = Calendar.current.dateComponents([.year, .month, .day], from: nextTriggerDate)
comps.day = 25
comps.hour = 12
comps.minute = 17
comps.second = 10

这是 comps 变量的 PO

var comps = Calendar.current.dateComponents([.year, .month, .day], 来自:下一个触发日期) comps.day = 25 comps.hour = 12 comps.minute = 17 comps.second = 10

我只给出了日期以查看它是否触发,我只给出了日期和时间以查看它是否触发并且我正在主线程中执行整个操作但它不工作

谁能让我明白我在这里做错了什么

【问题讨论】:

【参考方案1】:

您可能没有先请求许可

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

运行此代码。首先单击“请求权限”,然后单击“添加自定义触发器”,当您单击“打印”时,您应该会在控制台中看到您的通知

【讨论】:

【参考方案2】:

我已经浏览了您的代码,很明显您正在正确添加日历触发器,并且您在其中一个 cmet 中提到您在添加时也获得了必要的权限。

对我来说,这感觉像是一些日期时间问题。检查日期时间到日期时间组件的转换是否正确。

如果您在模拟器上进行测试,当您来回更改系统中的日期时,事情可能会变得很疯狂。

我建议你用真机测试。

这就是你获得许可的方式

  UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.announcement,.sound])  [self]
                    (granted, error) in
                    if granted 
                 print("granted")
                     else 
                 print("denied or error")
                    
                

您可以使用以下代码查询可用通知列表,以了解它是否在预期日期时间正确安排。

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)
                    

更新(下面的Credit(lorem ipsum)来自问题中的这个answer)

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

调用 getFireDatetime 方法,我猜这应该可以解决您的所有问题

附言

Date() 它为您提供通用时间的时间,而 Datetime 组件将为您提供设备当前日历设置的日期时间。

当您使用日期时间组件时,应自动处理时区更改和夏令时

【讨论】:

在系统中更改日期时间后在模拟器中尝试此选项,无论新更改的系统日期时间是否反映在模拟器中设备->重新启动,它可能会有所帮助跨度> 谢谢,我认为重启起到了神奇的作用:) 如果你能帮助我解决其他问题,那就太好了 @Amu 复制另一个答案的片段并将它们作为您自己的答案发布是非常阴暗的。你应该支持我的而不是只复制一半。 @Amu 假设我从本月 31 日开始重复。下个月是否会错过,因为它将有一个 30 日,如果下个月是 2 月会发生什么 如果您使用上面的代码 sn-p ,如果 31 日不可用,它将在下个月的结束日触发通知。这是 ios 的行为。

以上是关于为啥本地通知未针对 UNCalendarNotificationTrigger 触发的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 推送通知 iOS:未注册。为啥?

为啥颤振本地通知会出现异常

为啥只有我最后一个本地通知功能被调用?

为啥在 iOS 7 中我的本地通知没有显示横幅

为啥使用 laravel vue 在桌面通知中出现错误“未捕获的 ReferenceError:Vue 未定义”?

为啥这段代码没有抛出未定义的属性 PHP 通知?