WKRefreshBackgroundTask не работает
Я пытаюсь запустить приложение для часов в фоновом режиме. Для начала я скопировал пример из этого руководства для яблок:
Но это не работает вообще. Это мой код:
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController, WKExtensionDelegate, URLSessionDownloadDelegate {
@IBOutlet var unlink: WKInterfaceLabel!
@IBAction func startActivity() {
// fire in 20 seconds
let fireDate = Date(timeIntervalSinceNow: 20.0)
// optional, any SecureCoding compliant data can be passed here
let userInfo = ["reason" : "background update"] as NSDictionary
WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: fireDate, userInfo: userInfo) { (error) in
if (error == nil) {
print("successfully scheduled background task, use the crown to send the app to the background and wait for handle:BackgroundTasks to fire.")
}
}
}
let sampleDownloadURL = URL(string: "http://devstreaming.apple.com/videos/wwdc/2015/802mpzd3nzovlygpbg/802/802_designing_for_apple_watch.pdf?dl=1")!
// MARK: WKInterfaceController
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
WKExtension.shared().delegate = self
updateDateLabel()
}
let sampleDownloadURL = URL(string: "http://devstreaming.apple.com/videos/wwdc/2015/802mpzd3nzovlygpbg/802/802_designing_for_apple_watch.pdf?dl=1")!
// MARK: WKExtensionDelegate
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task : WKRefreshBackgroundTask in backgroundTasks {
print("received background task: ", task)
// only handle these while running in the background
if (WKExtension.shared().applicationState == .background) {
if task is WKApplicationRefreshBackgroundTask {
// this task is completed below, our app will then suspend while the download session runs
print("application task received, start URL session")
scheduleURLSession()
}
}
else if let urlTask = task as? WKURLSessionRefreshBackgroundTask {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: urlTask.sessionIdentifier)
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
print("Rejoining session ", backgroundSession)
}
// make sure to complete all tasks, even ones you don't handle
task.setTaskCompleted()
}
}
// MARK: Snapshot and UI updating
func scheduleSnapshot() {
// fire now, we're ready
let fireDate = Date()
WKExtension.shared().scheduleSnapshotRefresh(withPreferredDate: fireDate, userInfo: nil) { error in
if (error == nil) {
print("successfully scheduled snapshot. All background work completed.")
}
}
}
func updateDateLabel() {
let currentDate = Date()
self.unlink.setHidden(false)
unlink.setText(dateFormatter.string(from: currentDate))
}
// MARK: URLSession handling
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("NSURLSession finished to url: ", location)
updateDateLabel()
scheduleSnapshot()
}
func scheduleURLSession() {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: NSUUID().uuidString)
backgroundConfigObject.sessionSendsLaunchEvents = true
let backgroundSession = URLSession(configuration: backgroundConfigObject)
let downloadTask = backgroundSession.downloadTask(with: sampleDownloadURL)
downloadTask.resume()
}
}
Он печатает успешно запланированную фоновую задачу, использует корону, чтобы отправить приложение в фоновый режим, и ждет обработки дескриптора:BackgroundTasks, затем я отправляю приложение в фоновый режим, но никогда не использую метод handle.
Любая помощь?! Спасибо!!!
2 ответа
Вы установили предпочитаемую дату до 10 секунд. Как сказал Apple, нет никакой гарантии, что так быстро справиться с огнем.
preferredFireDate
Время следующей фоновой задачи обновления снимка. Система делает все возможное, чтобы разбудить ваше приложение в фоновом режиме в определенный момент после запланированного времени, но точное время не гарантируется.
Когда я установил этот параметр на 30 секунд, мне пришлось ждать около 10 минут, пока система не вызовет метод handle.
редактировать
Фоновые задачи обновления приложения заложены в бюджет. В целом, система выполняет примерно одну задачу в час для каждого приложения в док-станции (включая последнее использованное приложение). Этот бюджет распределяется между всеми приложениями на док-станции. Система выполняет несколько задач в час для каждого приложения с усложнением на активном циферблате. Этот бюджет распределяется между всеми сложностями на циферблате. После исчерпания бюджета система задерживает ваши запросы до тех пор, пока не станет доступно больше времени.
Я так и не получил пример с Apple. Но я получил фоновое обновление, работая со следующим кодом. Обратите внимание, что в отличие от примера Apple, вам нужно будет сделать себя делегатом в фоновом сеансе. (Также вам не нужно "я" в Swift, я просто использую его здесь, чтобы указать свойства / функции).
var backgroundUrlSession:URLSession?
var pendingBackgroundURLTask:WKURLSessionRefreshBackgroundTask?
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
if let refreshTask = task as? WKApplicationRefreshBackgroundTask {
// this task is completed below, our app will then suspend while the download session runs
print("application task received, start URL session")
if let request = self.getRequestForRefresh() {
let backgroundConfig = URLSessionConfiguration.background(withIdentifier: NSUUID().uuidString)
backgroundConfig.sessionSendsLaunchEvents = true
backgroundConfig.httpAdditionalHeaders = ["Accept":"application/json"]
//Be sure to set self as delegate on this urlSession
let urlSession = URLSession(configuration: backgroundConfig, delegate: self, delegateQueue: nil)
let downloadTask = urlSession.downloadTask(with: request)
print("Dispatching data task at \(self.getTimestamp())")
downloadTask.resume()
}
self.scheduleNextBackgroundRefresh(refreshDate: self.getNextPreferredRefreshDate())
refreshTask.setTaskCompleted()
}
else if let urlTask = task as? WKURLSessionRefreshBackgroundTask
{
//awakened because background url task has completed
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: urlTask.sessionIdentifier)
//Be sure to set self as delegate on this urlSession
self.backgroundUrlSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil) //set to nil in task:didCompleteWithError: delegate method
print("Rejoining session ", self.backgroundUrlSession as Any)
self.pendingBackgroundURLTask = urlTask //Saved for .setTaskComplete() in downloadTask:didFinishDownloadingTo location: (or if error non nil in task:didCompleteWithError:)
} else {
//else different task, not handling but must Complete all tasks (snapshot tasks hit this logic)
task.setTaskCompleted()
}
}
}
func getRequestForRefresh() -> URLRequest? {
guard let url = URL(string: "https://pokeapi.co/api/v2/pokemon") else{
return nil
}
return URLRequest(url: url)
}
// MARK: URLSession handling
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("NSURLSession finished to url: ", location)
updateDateLabel()
scheduleSnapshot()
self.pendingBackgroundURLTask.setTaskCompleted()
self.backgroundUrlSession = nil
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
self.pendingBackgroundURLTask.setTaskCompleted()
self.backgroundUrlSession = nil
}
}
Также без отпечатков / точек останова я иногда выполнял задание: ошибка didCompleteWithError: как мс до того, как я нажал downloadTask:didFinishDownloadingTo location:.
Поэтому я вместо этого установил self.pendingBackgroundURLTask, выполненный в downloadTask:didFinishDownloadingTo location:. Я только устанавливаю это выполненным в задаче:didCompleteWithError Ошибка: если ошибка!= Ноль. Я обновил свой ответ, чтобы включить эту логику также.
Надеюсь, что это помогает вам. Это работает в производстве для нас.