每 n 天安排一次本地通知(时区安全)

Posted

技术标签:

【中文标题】每 n 天安排一次本地通知(时区安全)【英文标题】:Schedule local notification every n days (timezone safe) 【发布时间】:2021-08-12 23:16:07 【问题描述】:

我相信这个问题已经被问过好几次了,但没有明确的答案。

有两种安排临时通知的方法:UNCalendarNotificationUNTimeIntervalNotificationTrigger

将通知安排在一周中的特定时间和日期是微不足道的,与在一个月中的特定日期相同,但在特定时间安排一次通知,每 n 天并不是那么简单。

例如每 5 天晚上 11:00。

UNTimeIntervalNotificationTrigger 似乎是适合的课程,除非在夏令时或时区更改时引入问题。例如。夏季时间结束,现在您的通知时间是 10:00 而不是 11:00。

DateComponents 类上的 day 属性与 UNCalendarNotification 类一起可能是解决方案,因为它在文档中说“一天天数强>”。我将此解释为“一个月中的特定日期(一天)或 n 天(count of days)”。

进一步挖掘day property docs,它显示“此值在使用它的日历的上下文中进行解释”。

如何将day 属性与日历上下文一起使用,以处理天数而不是一个月中的特定天数?

此外,DateComponents 中的 hourminute 属性的文档也分别读取“一小时或几小时”和“一分钟或几分钟”。因此,即使您将day 设置为“天数”,您如何正确设置hourminute

很明显,这个功能在 ios 中是可行的——提醒应用就是证明。

【问题讨论】:

每隔 n 天,基于什么日期?只要您坚持相同的时区或根本不设置时差,就没有区别。 @ElTomato 感谢您的评论,是的“每 n 天”需要一个锚定日期,但是您如何设置锚定日期?我知道calendar 属性可以在DateComponents 上设置,但是当将day 属性从“一天”设置为“天数”时,这会改变上下文吗? date(byAdding:to:options:)。你可以使用.day 【参考方案1】:

您可以使用UNCalendarNotificationTrigger 预先设置它们的次数为n,并使用调整后的日历作为当前时区

import SwiftUI

class NotificationManager: NSObject, UNUserNotificationCenterDelegate
    static let shared: NotificationManager = NotificationManager()
    let notificationCenter = UNUserNotificationCenter.current()
    
    private override init()
        super.init()
        requestNotification()
        notificationCenter.delegate = self
        getnotifications()
    
    
    func requestNotification() 
        print(#function)
        notificationCenter.requestAuthorization(options: [.alert, .sound, .badge])  granted, error in
            
            if let error = error 
                // Handle the error here.
                print(error)
            
            
            // Enable or disable features based on the authorization.
        
    
    /// Uses [.day, .hour, .minute, .second] in current timeZone
    func scheduleCalendarNotification(title: String, body: String, date: Date, repeats: Bool = false, identifier: String) 
        print(#function)
        
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        
        let calendar = NSCalendar.current

        let components = calendar.dateComponents([.day, .hour, .minute, .second], from: date)
        
        let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: repeats)
        
        let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
        notificationCenter.add(request)  (error) in
            if error != nil 
                print(error!)
            
        
    
    ///Sets up multiple calendar notification based on a date
    func recurringNotification(title: String, body: String, date: Date, identifier: String, everyXDays: Int, count: Int)
        print(#function)
        for n in 0..<count
            print(n)
            let newDate = date.addingTimeInterval(TimeInterval(60*60*24*everyXDays*n))
            //Idenfier must be unique so I added the n
            scheduleCalendarNotification(title: title, body: body, date: newDate, identifier: identifier + n.description)
            print(newDate)
        
    
    ///Prints to console schduled notifications
    func getnotifications()
        notificationCenter.getPendingNotificationRequests  request in
            for req in request
                if req.trigger is UNCalendarNotificationTrigger
                    print((req.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
                
            
        
    
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 
        
        completionHandler(.banner)
    

class ZuluNotTriggerViewModel:NSObject, ObservableObject, UNUserNotificationCenterDelegate
    @Published var currentTime: Date = Date()
    let notificationMgr = NotificationManager.shared
    
    
    ///Sets up multiple calendar notification based on a date
    func recurringNotification(title: String, body: String, date: Date, identifier: String, everyXDays: Int, count: Int)
        print(#function)
        notificationMgr.recurringNotification(title: title, body: body, date: date, identifier: identifier, everyXDays: everyXDays, count: count)
        
        //just for show now so you can see the current date in ui
        self.currentTime = Date()
    
    ///Prints to console schduled notifications
    func getnotifications()
        notificationMgr.getnotifications()
    
    

struct ZuluNotTriggerView: View 
    @StateObject var vm: ZuluNotTriggerViewModel = ZuluNotTriggerViewModel()
    var body: some View 
        VStack
            Button(vm.currentTime.description, action: 
                vm.currentTime = Date()
            )
            Button("schedule-notification", action: 
                let twoMinOffset = 120
                //first one will be in 120 seconds
                //gives time to change settings in simulator
                //initial day, hour, minute, second
                let initialDate = Date().addingTimeInterval(TimeInterval(twoMinOffset))
                //relevant components will be day, hour minutes, seconds
                vm.recurringNotification(title: "test", body: "repeat body", date: initialDate, identifier: "test", everyXDays: 2, count: 10)
            )
            
            Button("see notification", action: 
                vm.getnotifications()
            )
        
    


struct ZuluNotTriggerView_Previews: PreviewProvider 
    static var previews: some View 
        ZuluNotTriggerView()
    

【讨论】:

嗨 Lorem ipsum,感谢您提供如此详细的答案。答案的原始悬赏在我有机会奖励你之前就过期了,我已经开始了一个新的悬赏,并打算在按钮可用后奖励它。

以上是关于每 n 天安排一次本地通知(时区安全)的主要内容,如果未能解决你的问题,请参考以下文章

安排每小时重复本地通知的问题

如何安排本地通知不同的时间段?

最小化应用程序iOS时如何每隔特定N天推送本地通知?

iOS 11-每 x 分钟重复一次的用户本地通知

iOS 后台获取本地通知

重复本地通知 iOS 10