简体   繁体   中英

Http request from iOS push notification action when app is in background

My iOS application can receive push notifications with action buttons:

带有操作按钮的推送通知

Button tap sends post http request via UrlSession and data task. It works well when application is suspended or not running. But it doesn't work when app is in background eg when user just now swiped up from bottom (or pressed home button). Push action handles but http request not sends.

From console os_logs of my iPhone I see that UrlSession can't establish connection from background and throwing an error Code=-1005 "The network connection was lost.". It can as soon as I kill the app. I tried different url session configurations, background url session, background tasks but nothing works. The only workaround I found is to use exit(0) from applicationDidEnterBackground method, but it's highly not recommended to kill app programmatically.

Question : how can I establish http connection from push notification action when app is in background?

My code:

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()
    }
}

Finally I found an answer. Reason of unstable push notification actions work is wrong way to call completionHandler from userNotificationCenterDidReceiveResponse method. When perform asynchronous operation like http request you have to call completionHandler right after operation completes (not after operation's call) and from the main thread . Here is working example:

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()
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM