iOS本地推送:如何按指定日期、工作日定时并排除当日已完成目标的通知?
iOS本地工作日定时推送问题修复方案
原代码核心问题分析
- 无效的星期值:
dateComponents.weekday = 2 + 7计算后为9,超出Calendar允许的星期范围(周日=1,周六=7),导致推送永远无法触发。 - 缺少特定时间配置:未设置推送的小时和分钟参数,仅配置了秒数,不符合“特定时间(时、分)调度”的需求。
- 未覆盖工作日范围:当前仅尝试设置单个星期值,无法实现周一到周五的循环推送。
- 缺失目标完成取消逻辑:没有处理用户完成当日目标后取消对应通知的逻辑。
修正后的完整代码
import UIKit import UserNotifications public extension Date { func noon(using calendar: Calendar = .current) -> Date { calendar.date(bySettingHour: 12, minute: 0, second: 0, of: self)! } func day(using calendar: Calendar = .current) -> Int { calendar.component(.day, from: self) } func weekday(using calendar: Calendar = .current) -> Int { calendar.component(.weekday, from: self) } func hour(using calendar: Calendar = .current) -> Int { calendar.component(.hour, from: self) } func minute(using calendar: Calendar = .current) -> Int { calendar.component(.minute, from: self) } func second(using calendar: Calendar = .current) -> Int { calendar.component(.second, from: self) } func adding(_ component: Calendar.Component, value: Int, using calendar: Calendar = .current) -> Date { calendar.date(byAdding: component, value: value, to: self)! } func monthSymbol(using calendar: Calendar = .current) -> String { calendar.monthSymbols[calendar.component(.month, from: self)-1] } func addDay(n: Int) -> Date { let calendar = Calendar.current return calendar.date(byAdding: .day, value: n, to: self)! } // 新增:判断当前日期是否为工作日(周一到周五) func isWorkday(using calendar: Calendar = .current) -> Bool { let weekday = self.weekday(using: calendar) return weekday >= 2 && weekday <= 6 } } class ViewController: UIViewController, UNUserNotificationCenterDelegate { private let categoryID = "Category" // 为每个工作日推送设置唯一ID前缀,方便后续精准取消 private let notificationIDPrefix = "workday_notification_" override func viewDidLoad() { super.viewDidLoad() UNUserNotificationCenter.current().delegate = self let actionShowSomething = UNNotificationAction(identifier: "ShowSomething", title: "查看详情", options: []) let category = UNNotificationCategory(identifier: categoryID, actions: [actionShowSomething], intentIdentifiers: [], options: []) UNUserNotificationCenter.current().setNotificationCategories([category]) } @IBAction func addNotification(_ sender: Any) { UNUserNotificationCenter.current().removeAllPendingNotificationRequests() UNUserNotificationCenter.current().getNotificationSettings { (notificationSettings) in switch notificationSettings.authorizationStatus { case .notDetermined: self.requestAuthorization(completionHandler: { (success) in guard success else { return } self.scheduleWorkdayNotifications() }) case .authorized: self.scheduleWorkdayNotifications() case .denied: print("应用未被允许显示通知") case .provisional, .ephemeral, .@unknown: break } } } private func requestAuthorization(completionHandler: @escaping (_ success: Bool) -> ()) { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (success, error) in if let error = error { print("请求通知权限失败:\(error.localizedDescription)") } completionHandler(success) } } // 重构:创建周一到周五的定时推送 private func scheduleWorkdayNotifications() { let notificationContent = UNMutableNotificationContent() notificationContent.title = "目标提醒" notificationContent.subtitle = "今日目标待完成" notificationContent.body = "记得完成今日的目标任务哦!" notificationContent.categoryIdentifier = categoryID let calendar = Calendar.current // 设置推送的特定时间(示例为每天18:00,可根据需求修改) let targetHour = 18 let targetMinute = 0 // 遍历周一到周五(对应Calendar的weekday值2-6) for weekday in 2...6 { var dateComponents = DateComponents() dateComponents.calendar = calendar dateComponents.weekday = weekday dateComponents.hour = targetHour dateComponents.minute = targetMinute dateComponents.second = 0 // 确保准点触发 // 创建重复触发的推送触发器 let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) // 每个工作日的推送设置唯一ID let requestID = "\(notificationIDPrefix)\(weekday)" let request = UNNotificationRequest(identifier: requestID, content: notificationContent, trigger: trigger) UNUserNotificationCenter.current().add(request) { (error) in if let error = error { print("添加推送请求失败:\(error.localizedDescription)") } } } } // 新增:用户完成当日目标时,取消当日的推送 func cancelTodayNotification() { let calendar = Calendar.current let todayWeekday = Date().weekday(using: calendar) // 仅工作日需要取消推送 guard todayWeekday >= 2 && todayWeekday <= 6 else { return } let targetID = "\(notificationIDPrefix)\(todayWeekday)" UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [targetID]) print("已取消今日的推送通知") } // 确保APP在前台时也能收到通知提示 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .sound]) } }
关键修正点说明
- 修正星期值范围:使用2-6对应周一到周五,符合Calendar的weekday定义。
- 添加特定时间配置:设置
hour和minute参数,实现指定时分的精准推送。 - 覆盖工作日推送:遍历周一到周五,为每个工作日创建独立的重复推送请求,确保工作日循环触发。
- 目标完成取消逻辑:新增
cancelTodayNotification()方法,通过当日星期值匹配对应推送ID,精准移除当日待推送请求。 - 前台通知支持:实现
userNotificationCenter:willPresent:withCompletionHandler:方法,确保APP在前台时也能收到通知。
使用说明
- 调用
addNotification方法即可调度所有工作日的定时推送。 - 用户完成当日目标后,调用
cancelTodayNotification方法即可取消当日的推送。
内容的提问来源于stack exchange,提问作者AMIT
相关产品推荐
相关产品推荐

