ios - util - en que porcentaje de bateria se debe cargar el iphone
Código de ejemplo de Apple para la actualización de fondo de la extensión WatchKit (1)
He estado buscando la respuesta a esta pregunta durante bastante tiempo, así que creo que estoy dispuesto a arriesgar algunos votos a la baja para publicarlo.
Básicamente, quiero que el código de muestra proporcionado por Apple para la actualización de fondo de Apple Watch realmente funcione (enlace y código a continuación).
He intentado tanto en el simulador como en un iPhone 6s con un Apple Watch Series 2, y las tareas en segundo plano nunca se completan con éxito hasta el momento en que se actualiza el tiempo. He intentado fijar la aplicación del reloj en el dock, y he intentado mantener la aplicación en primer plano y enviarla al fondo, tanto en el simulador como en el reloj real. Incluso intenté esperar casi un año para ver si Xcode o Apple Watch recibirían una actualización que lo hiciera funcionar.
¿Alguien ha modificado con éxito el código provisto por Apple para que funcione?
Puede descargar el proyecto completo de muestra ejecutable aquí: WatchBackgroundRefresh: Uso de WKRefreshBackgroundTask para actualizar las aplicaciones WatchKit en segundo plano
/*
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.")
}
}
}
}
Lo siguiente es la salida cuando se ejecuta en el simulador. Salida similar (pero no necesariamente exactamente igual) cuando se ejecuta en otras configuraciones:
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
Para cualquier persona que pueda encontrar esto, hubo 2 problemas que vi, ambos con la programación de URLSession. Con estos cambios, creo que el código de ejemplo de Apple realmente funciona, al menos en el simulador.
-El sampleDownloadURL
necesita ser seguro, por lo que es necesaria una URL con HTTPS. Éste funciona: https://api.weather.gov/points/42.3584,-71.0598/forecast
-Me parece que el delegado de la URLSession
nunca se configuró a self
, por lo que se corrigió el siguiente cambio:
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
El siguiente código de trabajo (aunque menos completo) fue muy útil: Novedades en watchOS 3: Tareas en segundo plano
Edición: Un problema más que puede ocurrir es que las tareas se completan inmediatamente después de recibirlas. En la práctica, deben guardarse (en una variable local, por ejemplo) y luego completarse después de que se complete todo el procesamiento de esa tarea. Para este código, creo que eso significa colgar en la WKApplicationRefreshBackgroundTask
y no llamar a setTaskCompleted()
hasta que justo después de la llamada a scheduleSnapshot
.