Apple WatchKit 扩展后台刷新示例代码

Posted

技术标签:

【中文标题】Apple WatchKit 扩展后台刷新示例代码【英文标题】:Apple Sample Code for WatchKit Extension Background Refresh 【发布时间】:2017-09-07 01:30:12 【问题描述】:

我一直在寻找这个问题的答案很长一段时间,所以我想我愿意冒着投反对票的风险来发布它。

基本上,我想让 Apple 提供的 Apple Watch 后台刷新示例代码真正起作用(下面的链接和代码)。

我已经在模拟器和带有 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.")
            
        
    


以下是在模拟器上运行时的输出。在其他配置下运行时的类似输出(但不一定完全相同):

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

【问题讨论】:

一些一般性建议:使用 Swift 命名约定,函数名称使用小写字母 (scheduleRefreshButtonTapped())。关于您的问题:请提供更多关于什么不起作用的上下文。调度成功了吗? handle(backgroundTasks:) 有没有被调用过?请更具体。 @DávidPásztor 谢谢,当我将任何可用的 Apple 代码合并到我自己的项目中时,我一定会修正命名约定。至于不起作用的细节,我已经编辑了问题以添加相应输出的示例。 您是否在连接真 iPhone 的手表模拟器或连接 iPhone 的真 Apple Watch 上尝试过此代码。 @AasimKhan 谢谢,是的,我有。 此演示的 Apple 示例代码已被删除。谁有副本的链接。我能找到的最接近的是 Microsoft-Xamarin 转换:docs.microsoft.com/en-us/xamarin/ios/watchos/platform/… 【参考方案1】:

对于可能发现此问题的任何人,我看到了 2 个问题,都与 URLSession 调度有关。通过这些更改,我认为 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()

【讨论】:

以上是关于Apple WatchKit 扩展后台刷新示例代码的主要内容,如果未能解决你的问题,请参考以下文章

Apple Watch 和 openParentApplication 在后台

Apple Watch - 如果我想自定义远程通知的发送方式,是不是需要添加 watchkit 扩展程序?

Apple Watch: WatchKit 应用程序要点

WatchKit 扩展:基于页面的层次结构,一目了然

xCode 排除用于提交的 watchkit 扩展

在 Apple WatchKit 中使用核心位置