当应用程序处于后台时,来自iOS推送通知操作的Http请求

问题描述

我的iOS应用程序可以通过操作按钮接收推送通知

push notifications with action buttons

按钮点击通过UrlSession和数据任务发送http请求。当应用程序挂起或未运行时,它可以很好地工作。但是当应用程序在后台运行时它不起作用当用户刚刚从底部向上滑动(或按下主屏幕按钮)时。推送操作句柄,但http请求未发送。

从我的iPhone的控制台os_logs中,我看到UrlSession无法从后台建立连接并抛出错误代码= -1005“网络连接丢失。”。我可以杀死应用程序。我尝试了不同的url会话配置,后台url会话,后台任务,但没有任何效果。我发现的唯一解决方法是使用exit(0)方法中的applicationDidEnterBackground,但是强烈建议您不要以编程方式杀死应用。

问题:当应用程序处于后台时如何通过推送通知操作建立http连接?

我的代码

class NotificationService: NSObject,UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,willPresent notification: UNNotification,withCompletionHandler completionHandler: @escaping (UNNotificationPresentationoptions) -> Void) {
        // notification processing if app is in foreground
        ...
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter,didReceive response: UNNotificationResponse,withCompletionHandler completionHandler: @escaping () -> Void) {
        let requestId = String(describing: response.notification.request.content.userInfo["requestId"] ?? "")
        switch response.actionIdentifier {
        case "CONFIRM_ACTION":
            confirmAuthenticationRequest(requestId: requestId,isConfirmed: true)
        case "REJECT_ACTION":
            confirmAuthenticationRequest(requestId: requestId,isConfirmed: false)
        default:
            // on notification tap
            ...
        }
        completionHandler()
    }

private func confirmAuthenticationRequest(requestId: String,isConfirmed: Bool) {
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        let json: [String: Any] = [
            "requestId": requestId,"deny": !isConfirmed
        ]
        request.httpBody = try? JSONSerialization.data(withJSONObject: json)
        request.addValue("application/json",forHTTPHeaderField: "content-type")
        
        let dataTask = urlSession.dataTask(with: request)
        dataTask.resume()
    }
}

解决方法

最后我找到了答案。 推送通知操作不稳定的原因是从completionHandler方法调用userNotificationCenterDidReceiveResponse的错误方法。当执行诸如http请求之类的异步操作时,您必须在操作完成后立即调用completionHandler (不是在操作调用后),并从主线程调用。这是工作示例:

class NotificationService: NSObject,UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,didReceive response: UNNotificationResponse,withCompletionHandler completionHandler: @escaping () -> Void) {
        let requestId = String(describing: response.notification.request.content.userInfo["requestId"] ?? "")
        switch response.actionIdentifier {
        case "CONFIRM_ACTION":
            confirmAuthenticationRequest(requestId: requestId,isConfirmed: true,completion: completionHandler)
        case "REJECT_ACTION":
            confirmAuthenticationRequest(requestId: requestId,isConfirmed: false,completion: completionHandler)
        default:
            // on notification tap
            ...
            completionHandler()
        }
    }

private func confirmAuthenticationRequest(requestId: String,isConfirmed: Bool,completion: @escaping () -> Void) {
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        let json: [String: Any] = [
            "requestId": requestId,"deny": !isConfirmed
        ]
        request.httpBody = try? JSONSerialization.data(withJSONObject: json)
        request.addValue("application/json",forHTTPHeaderField: "content-type")
        
        let dataTask = urlSession.dataTask(with: request) { _,_,_ in
            DispatchQueue.main.async {
                completion()
            }
        }
        dataTask.resume()
    }
}