结合WatchConnectivity和复杂功能

ph1*_*psG 1 swift watchos-2 apple-watch-complication watchconnectivity wcsession

我希望我的并发症能够通过Watch Connectivity从iPhone获取数据。我正在使用sendMessage即时消息技术。

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

在我的iPhone上的ViewController中:

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"])
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的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)
}
Run Code Online (Sandbox Code Playgroud)

我在手表上唯一看到的是“不是”。为什么并发症不显示接收到的数据?

小智 6

主要问题是您正在尝试在并发症控制器内进行异步调用。

sendMessage:调用之后的代码将您的回复处理程序获得响应之前执行。这就是为什么在收到答复之前,由于模板文本已设置,因此并发症显示为“不”。

稍后的某个时候,getCurrentTimelineEntryForComplication返回后,sendMessage将收到响应并调用回复处理程序,该处理程序将只设置respondedString,然后退出该块。

您应该避免做什么:

您应该考虑Apple的建议,而不要尝试在并发症控制器中获取任何数据。

数据源类的工作是为ClockKit尽快提供所有请求的数据。数据源方法的实现应尽量少。请勿使用数据源方法从网络中获取数据,计算值或执行任何可能延迟该数据传递的操作。如果您需要获取或计算并发症数据,请在iOS应用或WatchKit扩展的其他部分中进行处理,然后将数据缓存在并发症数据源可以访问的位置。数据源方法唯一要做的就是获取缓存的数据,并将其放入ClockKit所需的格式。

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

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

苹果提供了一种手表连接transferCurrentComplicationUserInfo方法,该方法将立即将复杂信息(的词典)从手机传输到手表。

当您的iOS应用接收到用于并发症的更新数据时,可以使用Watch Connectivity框架立即更新并发症。WCSession的transferCurrentComplicationUserInfo:方法将高优先级消息发送到您的WatchKit扩展,并根据需要唤醒以传递数据。接收到数据后,根据需要扩展或重新加载时间轴,以强制ClockKit从您的数据源请求新数据。

在手表方面,您可以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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

GitHub上已有几个使用此方法的项目。

如果您仍然希望从手表端请求数据:

您想将WCSession代码从复杂控制器中移出,到watch扩展中,并作为WKExtensioninit的一部分进行激活。

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

调用会话委托的回复处理程序时,可以使用我之前提供的更新并发症代码来重新加载并发症的时间轴。

如果您使用计划的并发症更新来触发此更新,则该特定方法的缺点是您将执行两次更新。第一次更新将启动数据请求,但没有任何新数据要使用。第二次(手动)更新发生在接收到数据之后,此时新数据将出现在时间轴上。

这就是为什么通过电话在后台提供数据的方法效果更好的原因,因为它只需要更新一次即可。