当应用程序处于后台时来自 iOS 推送通知操作的 Http 请求
Http request from iOS push notification action when app is in background
我的 iOS 应用程序可以接收带有操作按钮的推送通知:
点击按钮通过 UrlSession 和数据任务发送 post http 请求。无论是否挂起应用程序,它都能很好地工作 运行。但是当应用程序处于后台时它不起作用,例如当用户刚刚从底部向上滑动(或按下主页按钮)时。推送操作处理但 HTTP 请求未发送。
从我的 iPhone 的控制台 os_logs 我看到 UrlSession 无法从后台建立连接并抛出错误代码 =-1005“网络连接丢失。”。它可以在我终止应用程序后立即执行。我尝试了不同的 url 会话配置、后台 url 会话、后台任务,但没有任何效果。我发现的唯一解决方法是使用 applicationDidEnterBackground
方法中的 exit(0)
,但强烈不建议以编程方式终止应用程序。
问题:当应用程序处于后台时,如何通过推送通知操作建立 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()
}
}
终于找到答案了。
推送通知操作不稳定的原因是从 userNotificationCenterDidReceiveResponse
方法调用 completionHandler
的方式错误。当执行像 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()
}
}
我的 iOS 应用程序可以接收带有操作按钮的推送通知:
点击按钮通过 UrlSession 和数据任务发送 post http 请求。无论是否挂起应用程序,它都能很好地工作 运行。但是当应用程序处于后台时它不起作用,例如当用户刚刚从底部向上滑动(或按下主页按钮)时。推送操作处理但 HTTP 请求未发送。
从我的 iPhone 的控制台 os_logs 我看到 UrlSession 无法从后台建立连接并抛出错误代码 =-1005“网络连接丢失。”。它可以在我终止应用程序后立即执行。我尝试了不同的 url 会话配置、后台 url 会话、后台任务,但没有任何效果。我发现的唯一解决方法是使用 applicationDidEnterBackground
方法中的 exit(0)
,但强烈不建议以编程方式终止应用程序。
问题:当应用程序处于后台时,如何通过推送通知操作建立 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()
}
}
终于找到答案了。
推送通知操作不稳定的原因是从 userNotificationCenterDidReceiveResponse
方法调用 completionHandler
的方式错误。当执行像 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()
}
}