如何在Swift中读取本地通知的触发DateComponents?

huangapple go评论73阅读模式
英文:

How to read a local notification's trigger DateComponents in Swift?

问题

我的应用旨在每天提供一条具有特定内容的每日通知。我知道iOS可以存储多达64个未来预定的通知,因此我正在寻找一种方法来确保明天的通知始终准备好触发,即使用户在一天以上没有打开应用程序。

为此,在我的主ViewModel中,我有一个函数来实现这个目标,但首先它会取消所有待定通知以确保没有重复。然而,这显然不是最佳方法。

所以,我的问题是:我使用下面的类和函数列出所有待定通知。我可以访问触发器,但如何获取其中的实际日期,以便我可以执行一些逻辑检查(例如,仅删除未来2天以上的请求)?

class LocalNotificationManager {
    
    static let shared = LocalNotificationManager()
    var notifications = [Notification]()
    let NC = UNUserNotificationCenter.current()
    
    func listScheduledNotifications() {
        NC.getPendingNotificationRequests { notifications in
            
            for notification in notifications {
                print("通知 [\(notification.identifier)], \(String(describing: notification.trigger))")
                
            }
            print("待定通知: [\(notifications.count)]")
        }
    }
}

以下是此函数的示例输出:

通知 [2023-07-28_AB5DD5E7-45F6-47E7-8A98-7670C3C8214F],
Optional(<UNCalendarNotificationTrigger: 0x6000023f8040; 
dateComponents: <NSDateComponents: 0x600002198dd0> {
    Calendar Year: 2023
    Month: 7
    Day: 28
    Hour: 10
    Minute: 15, repeats: NO>)
英文:

My application is meant to deliver a daily notification with some content specific for that day. I know that iOS can store up to 64 future scheduled notifications, so I'm looking for a way to make sure that tomorrow's notification is always ready to fire, even if the user hasn't opened the app in more than a day.

To that end, in my main ViewModel I have a function that does that, but first it cancels all pending notifications to ensure there are no duplicates. However, this is obviously not the best approach.

So, here's my question: I use the class and function below to list all pending notifications. I can access the trigger, but how do I get the actual date in it, so that I can perform some logical check (e.g. only delete those requests that are 2+ days in the future)?

class LocalNotificationManager {
    
    static let shared = LocalNotificationManager()
    var notifications = [Notification]()
    let NC = UNUserNotificationCenter.current()
    
    func listScheduledNotifications() {
        NC.getPendingNotificationRequests { notifications in
            
            for notification in notifications {
                print(&quot;Notification [\(notification.identifier)], \(String(describing: notification.trigger))&quot;)
                
            }
            print(&quot;Pending notifications: [\(notifications.count)]&quot;)
        }
    }
}

Here's a sample output of this function:

Notification [2023-07-28_AB5DD5E7-45F6-47E7-8A98-7670C3C8214F],
Optional(&lt;UNCalendarNotificationTrigger: 0x6000023f8040; 
dateComponents: &lt;NSDateComponents: 0x600002198dd0&gt; {
    Calendar Year: 2023
    Month: 7
    Day: 28
    Hour: 10
    Minute: 15, repeats: NO&gt;)

答案1

得分: 1

你应该将notification.trigger转换为UNCalendarNotificationTrigger。这可能会失败,因为并非所有的触发器都基于日期组件。你应该决定如何处理不基于日期组件的触发器。

if let trigger = notification.trigger as? UNCalendarNotificationTrigger {
    // 检查下一次触发此通知的时间是否在两天内
} else {
    // 不基于日期组件...
}

至于如何检查下一次触发此通知的时间是否在两天内,你可以使用 nextDate(after:matching:matchingPolicy:repeatedTimePolicy:direction:) 来获取一个 Date,然后检查这个 Date 是否在2天内。

let calendar = Calendar.current
if let nextTriggerInstant = calendar.nextDate(after: Date(), matching: trigger, matchingPolicy: .strict) {
    if nextTriggerInstant.timeIntervalSinceNow > 60 * 60 * 48 { // 48小时
        // 删除通知
    }
}

请注意,上述代码检查nextTriggerInstant是否在接下来的48小时内。如果你想进行基于日期的计算(仅考虑年-月-日组件),可以使用 dateComponents(_:from:to:)

let dateComponentsNow = calendar.dateComponents([.year, .month, .day], from: Date())
let dateComponentsAtTrigger = calendar.dateComponents([.year, .month, .day], from: nextTriggerInstant)
let days = calendar.dateComponents([.day], from: dateComponentsNow, to: dateComponentsAtTrigger)
if days.day! > 2 {
    // 删除...
}
英文:

You should cast notification.trigger to UNCalendarNotificationTrigger. This may fail, as not all triggers are based on date components. You should decide what you want to do with the ones not based on date components.

if let trigger = notification.trigger as? UNCalendarNotificationTrigger {
    // check if the next time this notification triggers is within two days
} else {
    // not based on date components...
}

As for how you check if check if the next time this notification triggers is within two days, you can use nextDate(after:matching:matchingPolicy:repeatedTimePolicy:direction:) to get a Date, and then check if the Date is within 2 days.

let calendar = Calendar.current
if let nextTriggerInstant = calendar.nextDate(after: Date(), matching: trigger, matchingPolicy: .strict) {
    if nextTriggerInstant.timeIntervalSinceNow &gt; 60 * 60 * 48 { // 48 hours
        // delete the notification
    }
}

Note that the above checks if nextTriggerInstant is within the next 48 hours. If you want a date-based calculation (only looking at the year-month-day components), use dateComponents(_:from:to:).

let dateComponentsNow = calendar.dateComponents([.year, .month, .day], from: Date())
let dateComponentsAtTrigger = calendar.dateComponents([.year, .month, .day], from: nextTriggerInstant)
let days = calendar.dateComponents([.day], from: dateComponentsNow, to: trigger)
if days &gt; 2 {
    // delete...
}

huangapple
  • 本文由 发表于 2023年7月3日 15:42:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76602740.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定