每 n 天安排一次本地通知(时区安全)
Schedule local notification every n days (timezone safe)
我相信这个问题已经被问过好几次了,但没有明确的答案。
有两种安排临时通知的方法:UNCalendarNotification
and UNTimeIntervalNotificationTrigger
。
为特定时间和星期几安排通知是微不足道的,与在一个月的特定日期安排通知一样,但为特定时间安排通知,每 n 天并不那么微不足道。
例如每 5 天 11:00.
UNTimeIntervalNotificationTrigger
似乎是正确的 class,除非在夏令时或时区更改时引入问题。例如。夏令时结束,现在您的通知位于 10:00 而不是 11:00.
DateComponents
class 上的 day
属性 和 UNCalendarNotification
class 可能包含解决方案,因为它在文档“一天 或天数”。我将其解释为“一个月中的特定一天(一天)或 n 天(天数)”。
进一步挖掘 day
property docs,它显示“此值在使用它的日历的上下文中解释”。
如何将 day
属性 与日历的上下文一起使用来处理 天数 而不是特定日期月?
此外,DateComponents
中 hour
和 minute
属性的文档也分别显示为“一小时或数小时”和“一分钟或数分钟”。因此,即使您将 day
设置为“天数”,您如何正确设置 hour
和 minute
?
很明显,此功能在 iOS 中是可能的 - Reminders 应用就是证明。
您可以预先使用 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()
}
}
我相信这个问题已经被问过好几次了,但没有明确的答案。
有两种安排临时通知的方法:UNCalendarNotification
and UNTimeIntervalNotificationTrigger
。
为特定时间和星期几安排通知是微不足道的,与在一个月的特定日期安排通知一样,但为特定时间安排通知,每 n 天并不那么微不足道。
例如每 5 天 11:00.
UNTimeIntervalNotificationTrigger
似乎是正确的 class,除非在夏令时或时区更改时引入问题。例如。夏令时结束,现在您的通知位于 10:00 而不是 11:00.
DateComponents
class 上的 day
属性 和 UNCalendarNotification
class 可能包含解决方案,因为它在文档“一天 或天数”。我将其解释为“一个月中的特定一天(一天)或 n 天(天数)”。
进一步挖掘 day
property docs,它显示“此值在使用它的日历的上下文中解释”。
如何将 day
属性 与日历的上下文一起使用来处理 天数 而不是特定日期月?
此外,DateComponents
中 hour
和 minute
属性的文档也分别显示为“一小时或数小时”和“一分钟或数分钟”。因此,即使您将 day
设置为“天数”,您如何正确设置 hour
和 minute
?
很明显,此功能在 iOS 中是可能的 - Reminders 应用就是证明。
您可以预先使用 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()
}
}