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

iOS应用后台无法接收交互式本地通知问题求助

后台交互式通知调用OpenAI接口失败,无法返回通知

我已经在App中集成交互式本地通知,搭配OpenAI实现了聊天机器人,目标是让用户在App后台时也能通过交互式通知和机器人对话。

当用户回复通知时,我通过以下方法接收响应:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    if response.actionIdentifier == "replyAction" {
        if let textResponse = response as? UNTextInputNotificationResponse {
            userQueryText = textResponse.userText
            fetchGPTChatforResponse(prompt: userQueryText ?? "")
        }
    }
    completionHandler()
}

收到用户输入后,调用下面的方法请求GPT接口:

func fetchGPTChatforResponse(prompt: String) {
    Task {
        do {
            let gptText = try await APIService().sendPromptToGPT(prompt: prompt)
            await MainActor.run {
                sendInteractiveNotification(message: gptText)
            }
        } catch {
            print("Errrror")
        }
    }
}

遇到的问题:App处于后台时,会抛出以下网络错误,且无法触发回复通知:

nw_write_request_report [C2] Send failed with error "Socket is not connected"

nw_read_request_report [C1] Receive failed with error "Software caused connection abort" sendPromptToGPT

已做的后台模式配置:
在AppDelegate中实现了后台任务注册和调度:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                
    // 请求通知权限
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
        if granted {
            print("Notification permission granted")
        } else if let error = error {
            print("Notification permission denied: \(error.localizedDescription)")
        }
    }
    
    UNUserNotificationCenter.current().delegate = self
    let customCategory = createNotificationCategory()
    UNUserNotificationCenter.current().setNotificationCategories([customCategory])
    IQKeyboardManager.shared.enable = true
    registerBackgroundModes()
    return true
}

func registerBackgroundModes() {
    print("registerBackgroundModes")
    let appRefreshTaskId = "com.xyz.iOSApp.apprefresh"
    let appProcessingTaskId = "com.xyz.iOSApp.fetch"

    BGTaskScheduler.shared.register(forTaskWithIdentifier: appRefreshTaskId, using: nil) { task in
        self.fetchGPTChatforResponse(prompt: self.userQueryText ?? "")
        task.setTaskCompleted(success: true)
        self.scheduleAppRefresh()
    }

    BGTaskScheduler.shared.register(forTaskWithIdentifier: appProcessingTaskId, using: nil) { task in
        //Logger.shared.info("[BGTASK] Perform bg processing \(appProcessingTaskId)")
        self.fetchGPTChatforResponse(prompt: self.userQueryText ?? "")
        task.setTaskCompleted(success: true)
        self.scheduleBackgroundProcessing()
    }
}

func scheduleAppRefresh() {
    print("scheduleAppRefresh")
    let request = BGAppRefreshTaskRequest(identifier: "com.quilr.iOSApp.apprefresh")
    request.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60)
    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not schedule app refresh task \(error.localizedDescription)")
    }
}

func scheduleBackgroundProcessing() {
    print("scheduleBackgroundProcessing")
    let request = BGProcessingTaskRequest(identifier: "com.quilr.iOSApp.fetch")
    request.requiresNetworkConnectivity = true // 需要网络时设为true,默认false
    request.requiresExternalPower = true // 需要充电时设为true,默认false
    request.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60) // 5分钟后执行
    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not schedule image fetch: (error)")
    }
}

在SceneDelegate中进入后台时调度任务:

func sceneDidEnterBackground(_ scene: UIScene) {
    // 场景从前台转后台时调用
    // 用于保存数据、释放资源、存储状态信息
    (UIApplication.shared.delegate as? AppDelegate)?.scheduleAppRefresh()
    (UIApplication.shared.delegate as? AppDelegate)?.scheduleBackgroundProcessing()
    // 后台时保存上下文变更
    (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}

我已经在Xcode中添加了后台模式权限,但App后台时依然无法收到GPT返回的通知。


问题分析与解决方案

1. 绑定后台任务到通知响应流程

App后台时系统会限制普通网络请求的执行时间,直接调用fetchGPTChatforResponse会因进程被挂起导致请求中断。需要创建临时后台任务延长App活跃时间,确保GPT请求和通知发送完成:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    var bgTask: UIBackgroundTaskIdentifier = .invalid
    
    // 创建后台任务,申请额外执行时间
    bgTask = UIApplication.shared.beginBackgroundTask(withName: "GPTResponseTask") {
        // 任务超时,强制结束
        UIApplication.shared.endBackgroundTask(bgTask)
        bgTask = .invalid
        completionHandler()
    }
    
    if response.actionIdentifier == "replyAction" {
        if let textResponse = response as? UNTextInputNotificationResponse {
            let userQuery = textResponse.userText
            Task {
                do {
                    let gptText = try await APIService().sendPromptToGPT(prompt: userQuery)
                    await MainActor.run {
                        sendInteractiveNotification(message: gptText)
                    }
                } catch {
                    print("GPT请求失败: \(error.localizedDescription)")
                    // 可选:发送错误提示通知
                    await MainActor.run {
                        sendInteractiveNotification(message: "请求失败,请稍后重试")
                    }
                }
                // 任务完成,结束后台任务
                UIApplication.shared.endBackgroundTask(bgTask)
                bgTask = .invalid
                completionHandler()
            }
        }
    } else {
        // 非回复动作,直接收尾
        UIApplication.shared.endBackgroundTask(bgTask)
        bgTask = .invalid
        completionHandler()
    }
}

2. 修正BGTaskScheduler的使用逻辑

你当前注册的定时后台任务(BGAppRefreshTask/BGProcessingTask)是用于周期性触发的场景,和“用户回复后立即执行”的需求不匹配。

  • 移除sceneDidEnterBackground中调度后台任务的代码,避免无效任务占用系统资源。
  • 若无需定时同步功能,可直接删除registerBackgroundModes、scheduleAppRefresh、scheduleBackgroundProcessing相关方法。

3. 验证后台权限配置

确保Xcode中Signing & Capabilities的Background Modes已勾选:

  • Remote notifications:保障通知相关的后台交互权限
  • Background fetch:可选,若后续有后台定时请求需求

4. 检查通知发送逻辑

确认sendInteractiveNotification方法的分类ID与创建的通知分类一致,否则通知无法正常显示交互控件:

func sendInteractiveNotification(message: String) {
    let content = UNMutableNotificationContent()
    content.title = "GPT回复"
    content.body = message
    content.categoryIdentifier = "你的交互分类ID" // 必须和createNotificationCategory中设置的ID一致
    content.sound = UNNotificationSound.default
    
    let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
    UNUserNotificationCenter.current().add(request) { error in
        if let error = error {
            print("发送通知失败: \(error.localizedDescription)")
        }
    }
}

5. 优化错误日志

修改fetchGPTChatforResponse的错误打印,输出具体错误信息,便于排查问题:

func fetchGPTChatforResponse(prompt: String) {
    Task {
        do {
            let gptText = try await APIService().sendPromptToGPT(prompt: prompt)
            await MainActor.run {
                sendInteractiveNotification(message: gptText)
            }
        } catch {
            print("GPT请求错误详情: \(error)")
        }
    }
}

内容的提问来源于stack exchange,提问作者iOSCoder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 16:34:59