WatchKit 扩展后台刷新的 Apple 示例代码
Apple Sample Code for WatchKit Extension Background Refresh
我一直在寻找这个问题的答案很长一段时间,所以我想我愿意冒着对 post 投反对票的风险。
基本上,我想让 Apple 提供的 Apple Watch 后台刷新示例代码真正起作用(link 和下面的代码)。
我已经在模拟器中和使用 Apple Watch Series 2 的 iPhone 6s 上都试过了,后台任务从未成功完成到时间更新的地步。我尝试将手表应用程序固定到扩展坞,并且尝试将应用程序保留在前台并将其发送到后台,无论是在模拟器中还是在实际手表上。我什至尝试等待将近一年,看看 Xcode 或 Apple Watch 是否会收到使其正常工作的更新。
有没有人成功修改 Apple 提供的代码使其工作?
您可以在此处下载整个 运行 可用示例项目:WatchBackgroundRefresh: Using WKRefreshBackgroundTask to update WatchKit apps in the background
/*
Copyright (C) 2016-2017 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The main interface controller.
*/
import WatchKit
import Foundation
class MainInterfaceController: WKInterfaceController, WKExtensionDelegate, URLSessionDownloadDelegate {
// MARK: Properties
let sampleDownloadURL = URL(string: "http://devstreaming.apple.com/videos/wwdc/2015/802mpzd3nzovlygpbg/802/802_designing_for_apple_watch.pdf?dl=1")!
@IBOutlet var timeDisplayLabel: WKInterfaceLabel!
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .long
return formatter
}()
// MARK: WKInterfaceController
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
WKExtension.shared().delegate = self
updateDateLabel()
}
// 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()
timeDisplayLabel.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()
}
// MARK: IB actions
@IBAction func ScheduleRefreshButtonTapped() {
// 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.")
}
}
}
}
在模拟器上运行时输出如下。在其他配置中 运行ning 时类似的输出(但不一定完全相同):
successfully scheduled background task, use the crown to send the app to the background and wait for handle:BackgroundTasks to fire.
received background task: <WKSnapshotRefreshBackgroundTask: 0x7b019030>
received background task: <WKApplicationRefreshBackgroundTask: 0x7a711290>
application task received, start URL session
对于可能发现此问题的任何人,我发现了 2 个问题,都与 URL 会话安排有关。通过这些更改,我认为 Apple 示例代码确实有效,至少在模拟器上是这样。
-sampleDownloadURL
需要安全,因此需要使用 HTTPS 的 URL。这个有效:https://api.weather.gov/points/42.3584,-71.0598/forecast
-在我看来 URLSession
的委托从未设置为 self
,因此以下更改修复了:
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
以下有效(虽然不太完整)代码非常有帮助:What's New in watchOS 3: Background Tasks
编辑:可能出现的另一个问题是任务在收到后立即完成。实际上,它们应该被保存(例如,在局部变量中),然后在该任务的所有处理完成后完成。对于这段代码,我认为这意味着挂在 WKApplicationRefreshBackgroundTask
上,直到调用 scheduleSnapshot
.
之后才调用 setTaskCompleted()
我一直在寻找这个问题的答案很长一段时间,所以我想我愿意冒着对 post 投反对票的风险。
基本上,我想让 Apple 提供的 Apple Watch 后台刷新示例代码真正起作用(link 和下面的代码)。
我已经在模拟器中和使用 Apple Watch Series 2 的 iPhone 6s 上都试过了,后台任务从未成功完成到时间更新的地步。我尝试将手表应用程序固定到扩展坞,并且尝试将应用程序保留在前台并将其发送到后台,无论是在模拟器中还是在实际手表上。我什至尝试等待将近一年,看看 Xcode 或 Apple Watch 是否会收到使其正常工作的更新。
有没有人成功修改 Apple 提供的代码使其工作?
您可以在此处下载整个 运行 可用示例项目:WatchBackgroundRefresh: Using WKRefreshBackgroundTask to update WatchKit apps in the background
/*
Copyright (C) 2016-2017 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The main interface controller.
*/
import WatchKit
import Foundation
class MainInterfaceController: WKInterfaceController, WKExtensionDelegate, URLSessionDownloadDelegate {
// MARK: Properties
let sampleDownloadURL = URL(string: "http://devstreaming.apple.com/videos/wwdc/2015/802mpzd3nzovlygpbg/802/802_designing_for_apple_watch.pdf?dl=1")!
@IBOutlet var timeDisplayLabel: WKInterfaceLabel!
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .long
return formatter
}()
// MARK: WKInterfaceController
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
WKExtension.shared().delegate = self
updateDateLabel()
}
// 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()
timeDisplayLabel.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()
}
// MARK: IB actions
@IBAction func ScheduleRefreshButtonTapped() {
// 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.")
}
}
}
}
在模拟器上运行时输出如下。在其他配置中 运行ning 时类似的输出(但不一定完全相同):
successfully scheduled background task, use the crown to send the app to the background and wait for handle:BackgroundTasks to fire.
received background task: <WKSnapshotRefreshBackgroundTask: 0x7b019030>
received background task: <WKApplicationRefreshBackgroundTask: 0x7a711290>
application task received, start URL session
对于可能发现此问题的任何人,我发现了 2 个问题,都与 URL 会话安排有关。通过这些更改,我认为 Apple 示例代码确实有效,至少在模拟器上是这样。
-sampleDownloadURL
需要安全,因此需要使用 HTTPS 的 URL。这个有效:https://api.weather.gov/points/42.3584,-71.0598/forecast
-在我看来 URLSession
的委托从未设置为 self
,因此以下更改修复了:
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
以下有效(虽然不太完整)代码非常有帮助:What's New in watchOS 3: Background Tasks
编辑:可能出现的另一个问题是任务在收到后立即完成。实际上,它们应该被保存(例如,在局部变量中),然后在该任务的所有处理完成后完成。对于这段代码,我认为这意味着挂在 WKApplicationRefreshBackgroundTask
上,直到调用 scheduleSnapshot
.
setTaskCompleted()