2017-09-07 8 views
19

J'ai cherché la réponse à cette question depuis assez longtemps, donc je pense que je suis prêt à risquer quelques downvotes pour l'afficher.Apple Exemple de code pour l'extension WatchKit Fond Actualiser

Fondamentalement, je veux que l'exemple de code fourni par Apple pour le rafraîchissement d'Apple Watch fonctionne (lien et code ci-dessous).

J'ai essayé à la fois dans le simulateur et sur un iPhone 6s avec une Apple Watch Series 2, et les tâches en arrière-plan ne sont jamais terminées avec succès au point où l'heure se met à 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. J'ai même essayé d'attendre près d'un an pour voir si Xcode ou l'Apple Watch recevrait une mise à jour qui le ferait fonctionner.

Est-ce que quelqu'un a réussi à modifier le code fourni par Apple pour le faire fonctionner?

Vous pouvez télécharger ici l'ensemble du projet exemple runnable: 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.") 
      } 
     } 
    } 

} 

Ce qui suit est sortie lorsqu'il est exécuté sur le simulateur. Une sortie similaire (mais pas nécessairement exactement la même chose) 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 
+0

Quelques conseils généraux: utilisez la convention de nommage Swift, qui est lower-camelCase pour les noms de fonctions ('scheduleRefreshButtonTapped()'). Concernant votre question: veuillez fournir plus de contexte quant à ce qui ne fonctionne pas. L'ordonnancement est-il réussi? Est-ce que 'handle (backgroundTasks:') est déjà appelé? S'il vous plaît soyez plus précis. –

+0

@ DávidPásztor Merci, je serai sûr de fixer les conventions de nommage quand j'incorpore n'importe quel code d'Apple fonctionnant dans mon propre projet. En ce qui concerne les détails de ce qui ne fonctionne pas, j'ai édité la question pour ajouter un échantillon de la sortie correspondante. – PerpetualStudent

+0

Avez-vous essayé ce code sur un simulateur de montre connecté avec un vrai iPhone ou sur une vraie montre Apple connectée à l'iPhone. –

Répondre

6

Pour tous ceux qui peuvent trouver cela, il y avait 2 problèmes que j'ai vu, les deux avec la programmation de URLSession. Avec ces changements, je pense que l'exemple de code d'Apple fonctionne, 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é du URLSession n'a jamais été mis à self, de sorte que le changement suivant fixe:

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

Ce qui suit travail (bien que moins complet) code a été très utile: What's New in watchOS 3: Background Tasks

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 sauvegardés (dans une variable locale, par exemple) puis complétés après que tout le traitement de cette tâche soit terminé. Pour ce code, je pense que cela signifie accrocher sur le WKApplicationRefreshBackgroundTask et ne pas appeler setTaskCompleted() jusqu'à ce que juste après l'appel à scheduleSnapshot.