web-dev-qa-db-fra.com

Apple Exemple de code pour l'actualisation en arrière-plan de l'extension WatchKit

Je cherche la réponse à cette question depuis assez longtemps, donc je pense que je suis prêt à risquer quelques downvotes pour la poster.

Fondamentalement, je veux que l'exemple de code fourni par Apple pour Apple Regarder l'actualisation en arrière-plan fonctionne réellement (lien et code ci-dessous).

J'ai essayé à la fois dans le simulateur et sur un iPhone 6s avec une montre Apple Watch 2, et les tâches d'arrière-plan ne sont jamais terminées avec succès au point où l'heure est mise à jour. J'ai essayé d'épingler l'application de montre sur le dock, et j'ai essayé de garder l'application au premier plan et de l'envoyer à l'arrière-plan, à la fois dans le simulateur et sur la montre réelle. J'ai même essayé d'attendre près d'un an pour voir si Xcode ou la montre Apple recevraient une mise à jour qui le ferait fonctionner.

Quelqu'un a-t-il réussi à modifier le code fourni par Apple pour le faire fonctionner?

Vous pouvez télécharger l'intégralité du projet d'exemple exécutable ici: WatchBackgroundRefresh: Utilisation de WKRefreshBackgroundTask pour mettre à jour les applications WatchKit en arrière-plan

 /*
 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.")
            }
        }
    }

}

Ce qui suit est sorti lorsqu'il est exécuté sur le simulateur. Sortie similaire (mais pas nécessairement exactement la même) lors de l'exécution dans d'autres 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
25
PerpetualStudent

Pour tous ceux qui peuvent trouver cela, il y a eu 2 problèmes que j'ai vus, tous les deux avec la planification d'URLSession. Avec ces changements, je pense que l'exemple de code Apple Apple fonctionne réellement, au moins sur le simulateur.

-Le sampleDownloadURL doit être sécurisé, donc une URL avec HTTPS est nécessaire. Celui-ci fonctionne: https://api.weather.gov/points/42.3584,-71.0598/forecast

-Il me semble que le délégué de URLSession n'a jamais été défini sur self, donc le changement suivant a corrigé cela:

let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)

Le code de travail suivant (bien que moins complet) était très utile: Quoi de neuf dans watchOS 3: Tâches d'arrière-plan

Modifier: Un autre problème qui peut se produire est que les tâches sont terminées immédiatement après leur réception. En pratique, ils doivent être enregistrés (dans une variable locale, par exemple), puis terminés une fois le traitement de cette tâche terminé. Pour ce code, je pense que cela signifie s'accrocher au WKApplicationRefreshBackgroundTask et ne pas appeler setTaskCompleted() dessus juste après l'appel à scheduleSnapshot.

6
PerpetualStudent