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

如何通过UserNotifications框架取消当日重复推送通知

基于UserNotifications取消当日已完成待办对应提醒的实现方案

核心思路很直接:给每日的提醒设置可预测的唯一标识符,待办全完成时按ID精准移除对应待投递通知即可,不要用随机ID、不要用跨天重复的循环通知,否则没法单独取消单日提醒。


1. 提前约定通知ID生成规则

不要给通知生成随机ID,统一用「固定前缀+当日日期字符串」的格式,比如dailyTodo_yyyyMMdd,例:2024年6月15日的提醒ID就是dailyTodo_20240615。
这样你不需要额外在本地存储通知ID,需要取消哪天的提醒,直接按规则拼出对应ID即可。

2. 调整每日通知的注册逻辑

不要用repeats: true的循环日历触发通知——这种通知是全局绑定的,一旦取消会把后续所有日期的提醒都删掉。改成每天为次日/当日注册单条非重复的定时通知,注册时严格按上面的规则设置ID:

import UserNotifications

// 调用示例:App启动时给当天、未来7天预注册提醒
func preloadUpcomingReminders() {
    for dayOffset in 0..<7 {
        guard let targetDate = Calendar.current.date(byAdding: .day, value: dayOffset, to: Date()) else { continue }
        scheduleSingleDayReminder(for: targetDate, remindHour: 20, remindMinute: 0)
    }
}

// 注册单日固定时间提醒
private func scheduleSingleDayReminder(for date: Date, remindHour: Int, remindMinute: Int) {
    let content = UNMutableNotificationContent()
    content.title = "今日待办提醒"
    content.body = "你还有未完成的待办事项,记得及时处理~"
    content.sound = .default
    
    // 构造目标提醒时间的日期组件
    var triggerComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
    triggerComponents.hour = remindHour
    triggerComponents.minute = remindMinute
    
    let trigger = UNCalendarNotificationTrigger(dateMatching: triggerComponents, repeats: false)
    // 按规则生成通知ID
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyyMMdd"
    let notificationId = "dailyTodo_\(formatter.string(from: date))"
    
    let request = UNNotificationRequest(identifier: notificationId, content: content, trigger: trigger)
    UNUserNotificationCenter.current().add(request) { error in
        if let error { print("单日提醒注册失败: \(error.localizedDescription)") }
    }
}

3. 待办全完成时触发取消逻辑

在待办状态变更的回调里(比如用户把当日最后一个待办标记为完成时),校验当日待办是否全部完成,若完成则直接移除对应ID的待投递通知即可:

func checkAndCancelTodayReminder() {
    // 替换成你自己项目里的待办校验逻辑:判断当日所有待办是否都已完成
    let todayTodos = TodoDataStore.shared.fetchTodayTodos()
    guard todayTodos.allSatisfy(\.isCompleted) else { return }
    
    // 生成当日通知ID
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyyMMdd"
    let todayNotificationId = "dailyTodo_\(formatter.string(from: Date()))"
    
    let center = UNUserNotificationCenter.current()
    // 移除还没到触发时间的当日提醒
    center.removePendingNotificationRequests(withIdentifiers: [todayNotificationId])
    // 可选:如果提醒已经触发过(在系统通知中心留了消息),也可以一起删掉
    center.removeDeliveredNotifications(withIdentifiers: [todayNotificationId])
}

注意事项

  • 不要调用removeAllPendingNotificationRequests(),会误删其他业务线的通知,必须按指定ID精准删除
  • 建议在App每次回到前台、待办状态发生变更时都调用一次上面的校验方法,避免状态不同步导致漏取消
  • 如果你之前已经上线了repeats: true的循环提醒,需要先调用移除方法把旧的循环通知清掉,再换成单天预注册的逻辑,否则会出现重复提醒
  • 提前做好通知权限申请,否则所有通知注册逻辑都不会生效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 10:01:28