You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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])
    }
}

关键修正点说明

  1. 修正星期值范围:使用2-6对应周一到周五,符合Calendar的weekday定义。
  2. 添加特定时间配置:设置hour和minute参数,实现指定时分的精准推送。
  3. 覆盖工作日推送:遍历周一到周五,为每个工作日创建独立的重复推送请求,确保工作日循环触发。
  4. 目标完成取消逻辑:新增cancelTodayNotification()方法,通过当日星期值匹配对应推送ID,精准移除当日待推送请求。
  5. 前台通知支持:实现userNotificationCenter:willPresent:withCompletionHandler:方法,确保APP在前台时也能收到通知。

使用说明

  • 调用addNotification方法即可调度所有工作日的定时推送。
  • 用户完成当日目标后,调用cancelTodayNotification方法即可取消当日的推送。

内容的提问来源于stack exchange,提问作者AMIT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 08:05:55