Swift中按指定日期时段调度本地通知及突破64条限制方案
解决方案
问题根源
- 触发器类型错误:原代码使用
UNTimeIntervalNotificationTrigger仅能触发单次通知,无法实现每周重复的指定星期几逻辑 - 星期几逻辑未整合到触发器:未将
daysOfWeek参数绑定到日历触发器的重复规则中 - 未处理iOS通知数量限制:iOS系统限制待调度通知总数不超过64条,直接调度140条会被系统丢弃
改进后的实现代码
import UserNotifications class NotificationScheduler { private let center = UNUserNotificationCenter.current() private let calendar = Calendar.current // 初始化时请求通知权限 func requestNotificationPermissions() { center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in if let error = error { print("权限请求失败: \(error.localizedDescription)") } } } /// 调度指定星期几、时间段内的重复通知 /// - Parameters: /// - numberOfNotificationsPerDay: 每个指定星期几发送的通知数量 /// - targetWeekdays: 目标星期几(1=周日, 2=周一, ...,7=周六) /// - startTime: 时间段开始时间(仅需hour和minute) /// - endTime: 时间段结束时间(仅需hour和minute) func scheduleRepeatingNotifications(numberOfNotificationsPerDay: Int, targetWeekdays: [Int], startTime: DateComponents, endTime: DateComponents) { // 验证时间范围有效性 guard let startHour = startTime.hour, let startMinute = startTime.minute, let endHour = endTime.hour, let endMinute = endTime.minute else { print("时间参数无效") return } let totalMinutes = (endHour - startHour) * 60 + (endMinute - startMinute) guard totalMinutes > 0 else { print("结束时间必须晚于开始时间") return } // 计算每条通知的间隔(确保至少1分钟) let interval = max(totalMinutes / numberOfNotificationsPerDay, 1) // 生成每个通知的时间偏移量(分钟) let timeOffsets = (0..<numberOfNotificationsPerDay).map { $0 * interval } // 先取消已存在的同类型通知,避免重复调度 center.removePendingNotificationRequests(withIdentifiers: getNotificationIdentifiers(targetWeekdays: targetWeekdays, offsets: timeOffsets)) // 计算当前可调度的最大数量,避免超过iOS限制 let maxBatchSize = 64 var pendingRequests = [UNNotificationRequest]() for weekday in targetWeekdays { for offset in timeOffsets { // 计算当前通知的具体时间点 let notificationHour = startHour + (offset / 60) let notificationMinute = startMinute + (offset % 60) // 构建日历触发器的日期组件 var triggerComponents = DateComponents() triggerComponents.weekday = weekday triggerComponents.hour = notificationHour triggerComponents.minute = notificationMinute // 创建通知内容 let content = UNMutableNotificationContent() content.title = "定时通知" content.body = "这是第\(offset/interval + 1)条通知" content.sound = .default // 生成唯一标识符,用于后续管理 let identifier = "weekday_\(weekday)_offset_\(offset)" // 创建重复的日历触发器 let trigger = UNCalendarNotificationTrigger(dateMatching: triggerComponents, repeats: true) let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger) pendingRequests.append(request) // 达到批量上限时立即调度,并清空队列 if pendingRequests.count >= maxBatchSize { center.add(pendingRequests) pendingRequests.removeAll() } } } // 调度剩余的请求 if !pendingRequests.isEmpty { center.add(pendingRequests) } // 设置通知触发回调,用于补充调度未来周期(如果需要超过64条的长期调度) center.delegate = self } /// 生成通知标识符列表 private func getNotificationIdentifiers(targetWeekdays: [Int], offsets: [Int]) -> [String] { var identifiers = [String]() for weekday in targetWeekdays { for offset in offsets { identifiers.append("weekday_\(weekday)_offset_\(offset)") } } return identifiers } } // 实现UNUserNotificationCenterDelegate,处理通知触发后的补充调度 extension NotificationScheduler: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 如果需要实现超过64条的长期调度(比如每周自动补充),可以在这里处理 // 例如:当一条通知触发后,调度下一个周期的对应通知(如果当前待调度数量不足) // 这里可以根据业务需求扩展逻辑 completionHandler() } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // 前台显示通知的逻辑 completionHandler([.alert, .sound]) } } // 使用示例 let scheduler = NotificationScheduler() scheduler.requestNotificationPermissions() // 示例:周三(4)、周六(7)的14:00-17:00之间发送20条通知 let startTime = DateComponents(hour: 14, minute: 0) let endTime = DateComponents(hour: 17, minute: 0) scheduler.scheduleRepeatingNotifications(numberOfNotificationsPerDay: 20, targetWeekdays: [4,7], startTime: startTime, endTime: endTime)
关键改进点说明
- 使用日历触发器:
UNCalendarNotificationTrigger结合weekday参数实现每周重复的指定星期几通知,解决原代码无法重复的问题 - 批量调度处理64条限制:将通知请求分批,每达到64条就立即调度,避免超过系统限制
- 通知标识符管理:为每个通知生成唯一标识符,方便后续取消或更新
- 权限请求:新增权限请求逻辑,确保应用拥有通知权限
- 回调扩展:实现
UNUserNotificationCenterDelegate,可在通知触发后补充调度未来周期的通知,支持超过64条的长期需求
注意事项
- 星期几的数值对应关系:
Calendar.current中weekday的取值为1=周日,2=周一,3=周二,4=周三,5=周四,6=周五,7=周六 - 如果需要精确控制长期调度(比如超过4周的通知),可以在
didReceive回调中,当通知触发后,重新调度下一个周期的对应通知,保证待调度数量始终不超过64条 - 测试时可通过Xcode的“Simulate Notifications”功能快速验证触发器逻辑
内容的提问来源于stack exchange,提问作者user24192912
相关产品推荐
相关产品推荐

