You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:34:56