WKRefreshBackgroundTask не работает

Я пытаюсь запустить приложение для часов в фоновом режиме. Для начала я скопировал пример из этого руководства для яблок:

https://developer.apple.com/library/content/samplecode/WatchBackgroundRefresh/Listings/WatchBackgroundRrefresh_WatchKit_Extension_MainInterfaceController_swift.html

Но это не работает вообще. Это мой код:

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 Ошибка: если ошибка!= Ноль. Я обновил свой ответ, чтобы включить эту логику также.

Надеюсь, что это помогает вам. Это работает в производстве для нас.

Другие вопросы по тегам