iOS本地通知触发监听及数据获取问题求助
你好!针对你在iOS本地通知里遇到的这个痛点——想在任何App状态下捕获通知触发时机,尤其是最后一条通知执行后续操作,我给你整理了一套适配当前UserNotifications框架的可行方案,完全不用那些废弃的API:
UNUserNotificationCenterDelegate + 通知标记 首先要明确,iOS针对不同App状态(前台、后台、未运行)提供了不同的通知回调入口,我们可以通过实现代理方法覆盖所有场景,同时在创建通知时给每条通知打上唯一标记,方便识别第五条(最后一条)通知。
1. 配置通知代理与权限
首先在App启动时设置通知中心的代理,并申请必要的通知权限,确保后续能收到回调:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 设置通知代理 UNUserNotificationCenter.current().delegate = self // 请求通知权限(按需添加选项) UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in if granted { print("通知权限已获取") } } return true }
2. 实现代理方法覆盖所有App状态
通过两个核心代理方法,分别处理前台收到通知和后台/未运行时用户点击通知的场景:
extension AppDelegate: UNUserNotificationCenterDelegate { // 【前台状态】通知触发时直接回调,无需用户点击 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo // 识别是否为第五条通知 if let index = userInfo["notificationIndex"] as? Int, index == 5 { handleLastNotificationTrigger() // 执行你的后续操作 } // 选择前台展示通知的方式(比如弹窗+声音) completionHandler([.alert, .sound]) } // 【后台/未运行状态】用户点击通知进入App时触发 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo if let index = userInfo["notificationIndex"] as? Int, index == 5 { handleLastNotificationTrigger() } completionHandler() } // 自定义的后续操作方法 private func handleLastNotificationTrigger() { print("第五条通知已触发,开始执行后续操作") // 这里写你的业务逻辑,比如更新本地数据、发起网络请求等 } }
3. 创建通知时添加唯一标记
修改你原来的通知创建代码,给每条通知的userInfo里添加索引标记,这样后续回调时就能识别出哪条是最后一条:
// 假设你从API拿到了5组时间数据,存在hoursArray和minutesArray里 for index in 1...5 { var dateComponents = DateComponents() dateComponents.weekday = Date().dayNumberOfWeek() dateComponents.hour = hoursArray[index-1] // 对应第index条通知的小时 dateComponents.minute = minutesArray[index-1] // 对应第index条通知的分钟 dateComponents.second = 5 let notificationContent = UNMutableNotificationContent() notificationContent.title = "每日提醒" notificationContent.body = "这是第\(index)条通知" // 添加索引标记到userInfo,用于后续识别 notificationContent.userInfo = ["notificationIndex": index] let notificationTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false) // 用索引作为通知标识,方便后续管理(比如取消特定通知) let request = UNNotificationRequest(identifier: "daily_notification_\(index)", content: notificationContent, trigger: notificationTrigger) UNUserNotificationCenter.current().add(request, withCompletionHandler: nil) }
4. 进阶:无需用户点击,后台/未运行时捕获通知触发
如果需要在用户不点击通知的情况下,也能在后台/未运行状态捕获到第五条通知的触发时机,可以使用UNNotificationServiceExtension——这是一个独立于主App的扩展,会在通知送达前被系统唤醒,适合执行轻量操作:
步骤1:创建Notification Service Extension
在Xcode中选择File > New > Target,选择Notification Service Extension,命名为NotificationHandlerExtension。
步骤2:实现扩展逻辑
修改自动生成的NotificationService类,在里面识别第五条通知并执行操作:
class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // 识别第五条通知 if let index = bestAttemptContent.userInfo["notificationIndex"] as? Int, index == 5 { // 执行轻量操作,比如记录触发时间到UserDefaults(主App后续可以读取) UserDefaults.standard.set(Date(), forKey: "lastNotificationTriggerTime") UserDefaults.standard.synchronize() } // 必须调用contentHandler返回通知内容(即使没修改) contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // 超时情况下返回原始通知内容 if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }
注意:这个扩展是独立进程,不能直接调用主App的方法,但可以通过UserDefaults、App Groups等方式共享数据,主App启动后读取这些数据来完成后续操作。
- 通知标识要唯一:用
daily_notification_1到daily_notification_5这样的命名,方便后续取消特定通知或者更新通知内容。 - iOS后台限制:如果需要执行的操作较重(比如长时间网络请求),可能需要结合后台任务,但轻量的状态记录、数据更新用上面的方案完全足够。
- 权限检查:确保用户授予了通知权限,否则所有回调都不会触发。
内容的提问来源于stack exchange,提问作者Yupi

