07-18-2023, 09:40 PM
I've been searching for the answer to this question for quite a long time, so I think I'm willing to risk some downvotes to post it.
Basically, I want to make the Apple provided sample code for Apple Watch background refresh actually work (link and code below).
I've tried both in the simulator and on an iPhone 6s with an Apple Watch Series 2, and the background tasks are never successfully completed to the point where the time updates. I've tried pinning the watch app to the dock, and I've tried keeping the app in the foreground and sending it to the background, both in the simulator and on the actual watch. I even tried waiting almost a year to see if Xcode or the Apple Watch would receive an update that would make it work.
Has anyone successfully modified the Apple provided code to make it work?
You can download the entire runnable sample project here: [WatchBackgroundRefresh: Using WKRefreshBackgroundTask to update WatchKit apps in the background][1]
/*
Copyright © 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.")
}
}
}
}
The following is output when run on the simulator. Similar output (but not necessarily exactly the same) when running in other configurations:
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
[1]:
Basically, I want to make the Apple provided sample code for Apple Watch background refresh actually work (link and code below).
I've tried both in the simulator and on an iPhone 6s with an Apple Watch Series 2, and the background tasks are never successfully completed to the point where the time updates. I've tried pinning the watch app to the dock, and I've tried keeping the app in the foreground and sending it to the background, both in the simulator and on the actual watch. I even tried waiting almost a year to see if Xcode or the Apple Watch would receive an update that would make it work.
Has anyone successfully modified the Apple provided code to make it work?
You can download the entire runnable sample project here: [WatchBackgroundRefresh: Using WKRefreshBackgroundTask to update WatchKit apps in the background][1]
/*
Copyright © 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.")
}
}
}
}
The following is output when run on the simulator. Similar output (but not necessarily exactly the same) when running in other configurations:
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
[1]:
[To see links please register here]