我已经集成了交互式本地通知。我使用OpenAI创建了一个聊机器人。我希望在应用处于后台时,通过交互式本地通知与聊机器人进行互动。当用户响应交互式本地通知时,我会通过以下函数接收用户的响应。
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()}
在从交互式通知接收到用户响应后,我会调用以下函数:
func fetchGPTChatforResponse(prompt: String) { Task { do { let gptText = try await APIService().sendPromptToGPT(prompt: prompt) await MainActor.run { sendInteractiveNotification(message: gptText) } } catch { print("Errrror") } }}
由于我的应用处于后台,我收到了以下错误:
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
并且没有触发本地通知。我还实现了后台模式功能,如下所示。
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.swift中,我像这样调用了后台刷新和后台处理函数:
func sceneDidEnterBackground(_ scene: UIScene) { // 当场景从前台过渡到后台时调用。 // 使用此方法保存数据,释放共享资源,并存储足够的场景特定状态信息 // 以便将场景恢复到当前状态。 (UIApplication.shared.delegate as? AppDelegate)?.scheduleAppRefresh() (UIApplication.shared.delegate as? AppDelegate)?.scheduleBackgroundProcessing() // 当应用过渡到后台时,保存应用的托管对象上下文中的更改。 (UIApplication.shared.delegate as? AppDelegate)?.saveContext()}
我还通过添加功能来启用了后台模式。但当应用处于后台时,我仍然没有收到本地通知。
回答:
您在完成工作之前就调用了委托方法提供的completionHandler
,这导致iOS暂停了您的应用,从而断开了网络连接。
您需要在fetchGPTChatforResponse
完成工作后再调用完成处理程序。您可以通过将Task
移动到委托方法中,并使用defer
来确保在所有工作完成后调用completionHandler
来重构您的代码。
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { Task { defer { completionHandler() } if response.actionIdentifier == "replyAction", let textResponse = response as? UNTextInputNotificationResponse { userQueryText = textResponse.userText do { try await fetchGPTChatforResponse(prompt:userQueryText) } catch { print(error) } } }}func fetchGPTChatforResponse(prompt: String) async throws { let gptText = try await APIService().sendPromptToGPT(prompt: prompt) await MainActor.run { sendInteractiveNotification(message: gptText) }}