#ios #swift #apple-push-notifications #background-process
#iOS #swift #apple -push-уведомления #фоновый процесс
Вопрос:
Мое приложение iOS может получать push-уведомления с помощью кнопок действий:
Нажатие кнопки отправляет post http-запрос через URLSession и data task. Он хорошо работает, когда приложение приостановлено или не запущено. Но это не работает, когда приложение находится в фоновом режиме, например, когда пользователь только что провел пальцем снизу вверх (или нажал кнопку «Домой»). Push-действие обрабатывает, но http-запрос не отправляет.
Из консоли os_logs моего iPhone я вижу, что URLSession не может установить соединение в фоновом режиме и выдает код ошибки =-1005 «Сетевое соединение было потеряно». Это может произойти, как только я убью приложение. Я пробовал разные конфигурации сеанса url, фоновый сеанс url, фоновые задачи, но ничего не работает. Единственное решение, которое я нашел, — это использовать exit(0)
applicationDidEnterBackground
метод from , но крайне не рекомендуется программно уничтожать приложение.
Вопрос: как я могу установить http-соединение из действия push-уведомления, когда приложение работает в фоновом режиме?
Мой код:
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()
}
}
Ответ №1:
Наконец я нашел ответ. Причиной нестабильной работы действий push-уведомлений является неправильный способ вызова completionHandler
userNotificationCenterDidReceiveResponse
метода from. При выполнении асинхронной операции, такой как 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()
}
}