1

我正在尝试让我的手表应用程序在后台模式下运行。为了开始这样做,我复制了这个苹果指南中的例子:

https://developer.apple.com/library/content/samplecode/WatchBackgroundRefresh/Listings/WatchBackgroundRrefresh_WatchKit_Extension_MainInterfaceController_swift.html#//apple_ref/doc/uid/TP40017295-WatchBackgroundRrefresh_WatchKit_Extension_MainInterfaceController_swift-DontLinkElementID_10

但它根本不起作用。这是我的代码:

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

}

它打印成功调度的后台任务,使用皇冠将应用程序发送到后台并等待 handle:BackgroundTasks 触发,然后我将应用程序发送到后台但从未进入处理方法。

有什么帮助吗?!谢谢!!!

4

2 回答 2

0

我也从来没有让苹果的例子起作用。但是我确实使用以下代码进行了后台刷新。请注意,与 Apple 的示例不同,您需要让自己成为后台会话的代表。(另外,在 Swift 中你并不真的需要“self.”,我只是在这里用它来表示属性/函数)。

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

同样没有打印/断点,我有时会点击 task:didCompleteWithError 错误:就像我点击 downloadTask:didFinishDownloadingTo location: 之前的毫秒。

所以我改为在 downloadTask:didFinishDownloadingTo location: 中设置 self.pendingBackgroundURLTask 完成。我只在任务中设置完成:didCompleteWithError 错误:如果错误!= nil。我已经更新了我的答案以包含这个逻辑。

希望这可以帮助你。它正在为我们生产。

于 2017-07-08T15:06:13.763 回答
0

您已将 preferredFireDate 设置为 10 秒。正如苹果所说,不能保证这么快就触发处理方法。

首选FireDate

下次后台快照刷新任务的时间。系统会尽一切努力在预定时间之后的某个时间点在后台唤醒您的应用程序,但不能保证准确的时间。

当我将此参数设置为 30 秒时,我不得不等待大约 10 分钟,直到系统调用了句柄方法。

编辑

后台应用刷新任务是有预算的。一般来说,系统大约每小时为 Dock 中的每个应用程序(包括最近使用的应用程序)执行一项任务。此预算在扩展坞上的所有应用程序之间共享。系统每小时为每个应用程序执行多项任务,并在活动表盘上显示复杂功能。此预算在表盘上的所有复杂功能中共享。在您用尽预算后,系统会延迟您的请求,直到有更多时间可用。

于 2018-08-03T12:10:58.643 回答