如何使用UNNotificationRequest设置每月最后一天触发的本地通知
问题原因
UILocalNotification 兼容 day = 0 代表「当月最后一天」的隐含规则,但UNCalendarNotificationTrigger 要求 DateComponents 的所有属性必须为合法的日期取值,day 的合法范围是 1 到对应月份的最大天数,设置为 0 后系统无法匹配到任何有效日期,因此触发器无效,nextTriggerDate 返回 nil。
解决方法
这里提供两种兼容不同场景的实现方案:
方案1:多日期匹配+触发校验(兼容所有 iOS 10+ 版本,最稳妥)
这种方式不会漏触发,也不需要动态更新通知,适合通知内容固定的场景:
- 注册 4 个触发器,分别匹配每月 28、29、30、31 日的 8 点
- 在通知收到时(可以通过
UNUserNotificationCenterDelegate的前台接收回调,或者Notification Service Extension处理后台/杀进程场景),判断当前日期是否为当月最后一天,是则展示通知,否则丢弃本次通知。
触发器注册示例代码:
// 替换为你自己的通知内容配置 let content = UNMutableNotificationContent() content.title = "月度提醒" content.body = "这是每月最后一天的通知" let targetDays = [28, 29, 30, 31] for day in targetDays { var components = DateComponents() components.hour = 8 components.minute = 0 components.day = day let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true) // 每个请求使用唯一ID避免覆盖 let request = UNNotificationRequest(identifier: "monthly_last_day_\(day)", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) }
最后一天校验逻辑示例:
func isLastDayOfMonth(date: Date = Date()) -> Bool { let calendar = Calendar.current guard let dayRange = calendar.range(of: .day, in: .month, for: date) else { return false } let currentDay = calendar.component(.day, from: date) return currentDay == dayRange.count }
方案2:动态注册单触发器(适合通知内容需要每月更新的场景)
每次触发当月的最后一天通知后,计算下一个月的最后一天日期,生成对应的 DateComponents 注册新的触发器即可,这种方式只需要维护 1 个通知请求。
下一个月最后一天计算示例:
func nextMonthLastDay8AM() -> Date? { let calendar = Calendar.current // 先取当前时间之后的第一个月首日 guard let nextMonthFirstDay = calendar.nextDate(after: Date(), matching: DateComponents(day: 1), matchingPolicy: .nextTime) else { return nil } // 月首日减1天即为当月最后一天 guard let lastDay = calendar.date(byAdding: .day, value: -1, to: nextMonthFirstDay) else { return nil } // 拼接8点的时间 return calendar.date(bySettingHour: 8, minute: 0, second: 0, of: lastDay) }
内容的提问来源于stack exchange,提问作者Bonan
相关产品推荐
相关产品推荐

