如何通过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
相关产品推荐
相关产品推荐

