结合 WatchConnectivity 和并发症

Combining WatchConnectivity and Complications

我希望我的复杂功能通过 Watch Connectivity 从 iPhone 获取数据。我正在使用 sendMessage 即时通讯技术。

我不想在尝试获取数据时打开我的 iPhone 应用程序,因此这需要在后台运行。

在我的 ViewController 我的 iPhone:

import UIKit
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {

var session: WCSession!

override func viewDidLoad() {
    super.viewDidLoad()
    if WCSession.isSupported() {
        self.session = WCSession.defaultSession()
        self.session.delegate = self
        self.session.activateSession()
    }
}

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
    if message.count != 1 { return }

    if message["request"] != nil {
        replyHandler(["response" : "data"])
    }
}

在我的 ComplicationController 中

var session: WCSession!

func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) {
    if complication.family != .ModularSmall {
        handler(nil)
    }

    if WCSession.isSupported() {
        self.session = WCSession.defaultSession()
        self.session.delegate = self
        self.session.activateSession()
    }

    var respondedString = "not"

    session.sendMessage(["request" : ""], replyHandler: {
        (resp) -> Void in
        respondedString = resp["response"]
    }, errorHandler: nil)

    let circularTemplate = CLKComplicationTemplateModularSmallSimpleText()
    circularTemplate.textProvider = CLKSimpleTextProvider(text: respondedString)
    let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: circularTemplate)
    handler(timelineEntry)
}

我手表上唯一能看到的是 "not"。为什么复杂功能不显示接收到的数据?

主要问题是您正试图在复杂控制器中进行异步调用。

您的 sendMessage: 调用之后的代码将在 在您的回复处理程序收到响应之前 执行。这就是为什么您的并发症显示 "not" 因为模板的文本已经设置,在您收到回复之前。

稍后,在 getCurrentTimelineEntryForComplication 返回后,sendMessage 将收到响应并调用回复处理程序,它只会设置 respondedString,然后退出该块。

你应该避免做什么:

您应该考虑 Apple's recommendations,不要尝试在复杂控制器中获取任何数据。

The job of your data source class is to provide ClockKit with any requested data as quickly as possible. The implementations of your data source methods should be minimal. Do not use your data source methods to fetch data from the network, compute values, or do anything that might delay the delivery of that data. If you need to fetch or compute the data for your complication, do it in your iOS app or in other parts of your WatchKit extension, and cache the data in a place where your complication data source can access it. The only thing your data source methods should do is take the cached data and put it into the format that ClockKit requires.

此外,您在数据源中执行的任何 activity 都将不必要地用完分配给您的并发症的每日执行时间预算。

如何为您的并发症提供数据?

Apple 提供了一个 Watch Connectivity transferCurrentComplicationUserInfo 方法,它将 将复杂功能信息(字典)从 phone 传输到手表。

When your iOS app receives updated data intended for your complication, it can use the Watch Connectivity framework to update your complication right away. The transferCurrentComplicationUserInfo: method of WCSession sends a high priority message to your WatchKit extension, waking it up as needed to deliver the data. Upon receiving the data, extend or reload your timeline as needed to force ClockKit to request the new data from your data source.

在手表方面,您有 WCSessionDelegate 句柄 didReceiveUserInfo 并使用收到的数据更新您的并发症:

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
    if let ... { // Retrieve values from dictionary

        // Update complication
        let complicationServer = CLKComplicationServer.sharedInstance()
        guard let activeComplications = complicationServer.activeComplications else { // watchOS 2.2
            return
        }

        for complication in activeComplications {
            complicationServer.reloadTimelineForComplication(complication)
        }
    }
}

Apple 工程师通常建议设置一个数据管理器来保存数据。在您的复杂控制器中,您将从数据管理器中检索最新信息以用于您的时间线。

GitHub 上有几个现有项目使用这种方法。

如果你还是喜欢从手表端请求数据:

您希望将 WCSession 代码从复杂功能控制器中移出,放入手表扩展中,并将其作为 WKExtension init 的一部分激活。

关键是让回复处理程序在收到数据后手动更新并发症。

当您的会话委托的回复处理程序被调用时,您可以使用我之前提供的更新复杂功能代码来重新加载您的复杂功能的时间线。

如果您使用预定的复杂功能更新来触发此操作,则该特定方法的缺点是您将执行两次更新。第一次更新会发起数据请求,但没有任何新数据可供使用。第二次(手动)更新发生在接收到数据之后,此时新数据将出现在时间线上。

这就是为什么 phone 在后台提供数据的方法效果更好,因为它只需要一次更新。