如何实现按单双日推送本地通知并向服务器回传确认?
技术咨询:如何实现按单双日推送本地通知并向服务器回传确认信息?
背景说明
我已在提醒功能(基于EventKit)中实现过类似的单双日提醒逻辑,具体代码如下:
/// Creates a reminder func create(_ title: String, isOddDays: Bool, hour: Int, minute: Int) { let dueDate = Date() let gregorian = Calendar(identifier: .gregorian) let daysOfTheMonth: [NSNumber] if isOddDays { daysOfTheMonth = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31] } else { daysOfTheMonth = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30] } let rule = EKRecurrenceRule( recurrenceWith: .monthly, interval: 1, daysOfTheWeek: nil, daysOfTheMonth: daysOfTheMonth, monthsOfTheYear: nil, weeksOfTheYear: nil, daysOfTheYear: nil, setPositions: nil, end: nil ) guard let reminder: EKReminder = self.setUpReminder(with: title, priority: 0) else { return } reminder.dueDateComponents = gregorian.dateComponents([.day, .month, .year, .hour, .minute, .second], from: dueDate) reminder.addRecurrenceRule(rule) let todayDateComponents = gregorian.dateComponents([.day, .month, .year], from: Date()) let day: Int = isOddDays ? 1 : 2 let month: Int = todayDateComponents.month! let year: Int = todayDateComponents.year! let absoluteDate = Date.create(day: day, month: month, year: year, hour: hour, minute: minute, second: 0) let alarm = EKAlarm(absoluteDate: absoluteDate) reminder.addAlarm(alarm) UserDefaults.standard.set(reminder.calendarItemIdentifier, forKey: ReminderStoreManager.reminderDefaultsKey) self.saveReminder(reminder) }
现希望用**本地通知(UserNotifications框架)**实现类似的单双日推送逻辑,完成后向服务器回传确认信息。
编辑补充:已添加本地通知配置界面截图(显示通知权限、触发时间等设置项)
实现方案
一、核心思路
由于UserNotifications不支持直接指定每月多日期的重复规则,采用「单次调度+触发后自动续期」的方式:
- 首次调度时计算下一个符合单双日条件的日期,创建单次本地通知
- 通知触发/用户点击后,立即调度下一个符合条件的通知
- 同时在触发回调中向服务器发送确认请求
二、具体实现步骤
1. 请求通知权限
先获取用户的本地通知权限:
import UserNotifications func requestNotificationPermission() { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in if granted { print("通知权限已获取") } else { print("通知权限被拒绝") } } }
2. 首次调度单双日通知
计算下一个符合条件的日期,创建单次本地通知:
func scheduleOddEvenDayNotification(title: String, isOddDays: Bool, hour: Int, minute: Int) { // 构建通知内容 let content = UNMutableNotificationContent() content.title = title content.body = "单双日提醒" content.sound = UNNotificationSound.default let gregorian = Calendar(identifier: .gregorian) // 找到下一个符合单双日条件的日期 var nextDate = Date() repeat { nextDate = gregorian.date(byAdding: .day, value: 1, to: nextDate)! let day = gregorian.component(.day, from: nextDate) } while (isOddDays && day % 2 == 0) || (!isOddDays && day % 2 != 0) // 创建触发条件 let triggerComponents = gregorian.dateComponents([.year, .month, .day, .hour, .minute], from: nextDate) let trigger = UNCalendarNotificationTrigger(dateMatching: triggerComponents, repeats: false) // 创建通知请求 let requestID = isOddDays ? "odd_day_notification" : "even_day_notification" let request = UNNotificationRequest(identifier: requestID, content: content, trigger: trigger) // 添加到通知中心 UNUserNotificationCenter.current().add(request) { error in if let error = error { print("添加通知失败:\(error.localizedDescription)") } } // 保存用户设置到UserDefaults,用于后续续期 UserDefaults.standard.set(isOddDays, forKey: "isOddDayNotification") UserDefaults.standard.set(hour, forKey: "notificationHour") UserDefaults.standard.set(minute, forKey: "notificationMinute") }
3. 处理通知触发与服务器回传
设置通知中心代理,在通知触发/用户点击时完成续期和回传:
// 在AppDelegate中设置代理 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().delegate = self requestNotificationPermission() checkAndRescheduleNotification() // App启动时检查并补全通知 return true } // 实现UNUserNotificationCenterDelegate协议 extension AppDelegate: UNUserNotificationCenterDelegate { // 前台收到通知时的处理 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // 显示通知 completionHandler([.alert, .sound]) // 回传服务器确认 sendConfirmationToServer(notificationID: notification.request.identifier) // 续期下一个通知 rescheduleNextNotification(from: notification.request.identifier) } // 用户点击通知后的处理 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 回传服务器确认 sendConfirmationToServer(notificationID: response.notification.request.identifier) // 续期下一个通知 rescheduleNextNotification(from: response.notification.request.identifier) completionHandler() } // 续期下一个符合条件的通知 func rescheduleNextNotification(from requestID: String) { let isOddDays = requestID == "odd_day_notification" let hour = UserDefaults.standard.integer(forKey: "notificationHour") let minute = UserDefaults.standard.integer(forKey: "notificationMinute") scheduleOddEvenDayNotification(title: isOddDays ? "奇数日提醒" : "偶数日提醒", isOddDays: isOddDays, hour: hour, minute: minute) } // 向服务器发送确认请求 func sendConfirmationToServer(notificationID: String) { guard let url = URL(string: "https://your-server-api.com/confirm-notification") else { return } var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") // 构建请求参数,替换为实际业务字段 let params: [String: Any] = [ "notification_id": notificationID, "user_id": "current_user_id", "trigger_time": Date().timeIntervalSince1970 ] do { request.httpBody = try JSONSerialization.data(withJSONObject: params, options: []) } catch { print("参数序列化失败:\(error.localizedDescription)") return } // 发送请求 URLSession.shared.dataTask(with: request) { _, response, error in if let error = error { print("回传失败:\(error.localizedDescription)") return } if let httpResp = response as? HTTPURLResponse, httpResp.statusCode == 200 { print("回传确认成功") } else { print("回传失败,状态码异常") } }.resume() } }
4. App启动时补全通知
避免App被杀死后通知中断,启动时检查并重新调度:
func checkAndRescheduleNotification() { UNUserNotificationCenter.current().getPendingNotificationRequests { requests in let isOddDays = UserDefaults.standard.bool(forKey: "isOddDayNotification") let targetID = isOddDays ? "odd_day_notification" : "even_day_notification" let hasScheduled = requests.contains { $0.identifier == targetID } if !hasScheduled { let hour = UserDefaults.standard.integer(forKey: "notificationHour") let minute = UserDefaults.standard.integer(forKey: "notificationMinute") self.scheduleOddEvenDayNotification(title: isOddDays ? "奇数日提醒" : "偶数日提醒", isOddDays: isOddDays, hour: hour, minute: minute) } } }
内容的提问来源于stack exchange,提问作者Idan Moshe
相关产品推荐
相关产品推荐

