更新Apple Watch的并发数据流是什么?

22

我一直在网上跟随很多教程学习如何设置复杂性。我没有问题按预期设置复杂性。

直到最初的时间轴条目过期。12小时后,我不知道如何更新它以保持复杂性活动状态。我将分享我所拥有的一切,希望有人能帮助我填补空白。

在这里,我创建了要在复杂性上显示的数据变量。

struct data = {
var name: String
var startString: String
var startDate: NSDate
}

以下数组是该数据的容器。
var dataArray = [data]

这使得在手表锁定时也能显示复杂功能。
func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
    handler(.ShowOnLockScreen)
}

这使得在该复杂性上进行向前的时间旅行成为可能。
func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
    handler([.Forward])
}

在这里,我将时间线的起始时间设为当前时间。

func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
    handler(NSDate())
}

在这里,我将时间轴的结束时间设置为现在的12小时后。

func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
    handler(NSDate(timeIntervalSinceNow: (60 * 60 * 12)))
}

在这里,我创建了复杂的模板。这是为了当用户在手表上浏览所有复杂性时,向他们展示样本数据。

func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {

    let headerTextProvider = CLKSimpleTextProvider(text: "Some Data")
    let body1TextProvider = CLKSimpleTextProvider(text: "Some Data Time")
    let template = CLKComplicationTemplateModularLargeStandardBody()
    template.headerTextProvider = headerTextProvider
    template.body1TextProvider = body1TextProvider

    handler(template)
}

这将创建第一个时间线条目以供复杂化使用。一旦启用了复杂化,此代码将运行并立即填充相应的复杂化。

func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void) {

    createData()

    if complication.family == .ModularLarge {

        if dataArray.count != 0 {

            let firstData = dataArray[0]
            let headerTextProvider = CLKSimpleTextProvider(text: firstData.name)
            let body1TextProvider = CLKSimpleTextProvider(text: firstData.startString)
            let template = CLKComplicationTemplateModularLargeStandardBody()
            template.headerTextProvider = headerTextProvider
            template.body1TextProvider = body1TextProvider
            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template)
            handler(timelineEntry)
        } else {
            let headerTextProvider = CLKSimpleTextProvider(text: "No Data")
            let body1TextProvider = CLKSimpleTextProvider(text: "Create some data")
            let template = CLKComplicationTemplateModularLargeStandardBody()
            template.headerTextProvider = headerTextProvider
            template.body1TextProvider = body1TextProvider

            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template)
            handler(timelineEntry)
        }

    } else {
        handler(nil)
    }

}

这是我创建时间轴条目的地方,用于管理我现有的所有数据。
func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {

    createData()

    var entries = [CLKComplicationTimelineEntry]()

    for dataObject in dataArray {

        if entries.count < limit && data.startDate.timeIntervalSinceDate(date) > 0 {

            let headerTextProvider = CLKSimpleTextProvider(text: dataObject.name)
            let body1TextProvider = CLKSimpleTextProvider(text: dataObject.startString)
            let template = CLKComplicationTemplateModularLargeStandardBody()
            template.headerTextProvider = headerTextProvider
            template.body1TextProvider = body1TextProvider
            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(timeInterval: (-10*60), sinceDate: data.startDate), complicationTemplate: template)
            entries.append(timelineEntry)

        }

    }

    handler(entries)

}

这告诉手表何时更新并显示复杂数据。

func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
    handler(NSDate(timeIntervalSinceNow: 60 * 60 * 6))
}

这就是我遇到问题的地方。

如何创建新的数据并重新加载时间轴?流程是什么?我不是试图扩展时间轴,而是要完全替换它。我完全不知道该怎么做。苹果文档在这一点上相当模糊。我知道需要实现以下方法,但不知道具体如何操作。有人能帮我填写这段代码吗?

func requestedUpdateDidBegin() {
    createData() //I assume createData() goes here? If so, how do I populate the new timeline entries based on the results?
}

func requestedUpdateBudgetExhausted() {
    //This can't possibly be the case as I haven't gotten it to work once.
}

func reloadTimelineForComplication(complication: CLKComplication!) {
      //This method appears to do nothing.
}

更新:

感谢El Tea的帮助,我已经解决了问题。我需要在requestedUpdateDidBegin中添加一个CLKComplicationServer实例,并将reloadTimeline方法放在其中。

下面是更新后的代码:

func requestedUpdateDidBegin() {
    print("Complication update is starting")

    createData()

    let server=CLKComplicationServer.sharedInstance()

    for comp in (server.activeComplications) {
        server.reloadTimelineForComplication(comp)
        print("Timeline has been reloaded!")
    }

}

func requestedUpdateBudgetExhausted() {
    print("Budget exhausted")
}

ComplicationController方面是否遇到过问题,无法通过WCSession:ExtensionDelegate中设置的数据获取它?基本上,在您createData()函数中的任何代码中,如果通过 let myDelegate = WKExtension.sharedExtension().delegate as!ExtensionDelegateExtensionDelegate 中获取,但实际上没有获取任何数据。我在这里卡住了:https://dev59.com/UpTfa4cB1Zd3GeqPPFFT - SRMR
2个回答

27

在时间间隔内进行的复杂刷新流程按照以下顺序进行:

  • iOS调用您的函数requestedUpdateDidBegin()requestedUpdateBudgetExhausted()(如果您的预算用完了,除非您获得更多执行时间,否则您不会更新任何内容)。
  • requestedUpdateDidBegin()中,您必须调用reloadTimelineForComplication()extendTimelineForComplication()来指定要重新加载或添加数据的哪个复杂情况。如果您不这样做,什么都不会发生!
  • 根据您是否调用了reloadextend,iOS会调用getCurrentTimelineEntryForComplication()getTimelineEntriesForComplication()中的一个或两个。
  • 无论您是否更新了您的复杂情况,iOS都会调用getNextRequestedUpdateDateWithHandler()来找出您下次想要重复上述步骤的时间。

注意:最后两个步骤不一定要按照这个顺序发生。

这个过程是这样工作的,以便iOS不会要求您反复生成相同的数据。它在requestedUpdateDidBegin()中给您一个机会,让您决定是否需要更新您的复杂情况。如果不需要更新,您的代码应该返回。 (这将减少您的复杂情况的执行时间,并帮助避免iOS在使用其每日预算时切断您的应用程序的进一步更新)。但是,如果您有新数据,您需要通过调用reloadTimelineForComplication()extendTimelineForComplication()来告诉iOS。

据我所知,除了您没有在requestedUpdateDidBegin()中请求重新加载或扩展之外,您写的所有内容看起来都很好。您的复杂情况可能会出现在手表表面上的多个位置,并且不同的模板具有不同的显示行为,因此您必须使它们全部失效。以下是我的代码:

func requestedUpdateDidBegin() {

    //Not shown: decide if you actually need to update your complication.
    //If you do, execute the following code:

    let server=CLKComplicationServer.sharedInstance()

    for comp in (server.activeComplications) {
        server.reloadTimelineForComplication(comp)
    }
}
请注意,除了时间间隔之外,还有其他方法可以启动刷新,包括推送警报、在手表应用程序运行时执行重新加载或使用 Watch Connectivity 框架与 WCSession 一起使用 transferCurrentComplicationUserInfo() 来使您的手机应用程序发送更新数据以便立即显示。有关更多信息,请参见苹果文档中的 “更新您的并发数据”
我已经成功在模拟器中测试了短至十分钟的更新间隔。由于执行时间预算,您可能不应该在真实手表上频繁更新,但这将允许您在等待12小时之前测试代码。

我正在尝试!可能是因为我没有包含CLKComplicationServer。如果最终能够工作,我会告诉你的。 - swiftyboi
4
太好了!复杂性很精妙,但现在关于它们的信息仍然非常缺乏。很高兴你让它运转起来了。 - El Tea
如果您的createData()函数是同步的,那么一切都很顺利,但如果它需要从iOS应用程序获取数据呢?在这种情况下,最好的方法是让iOS应用程序调用transferCurrentComlicationUserInfo吗?一种可行但有些hacky的替代方法是,在延迟计时器上调用requestedUpdateDidBegin来重新加载时间线,并在复杂化。 - RealCasually
未经测试,但我的方法取决于iOS应用程序上的数据是如何生成的。如果手机上的数据是自己生成的,并且以自己的速度进行生成,我认为我会像你说的那样使用transferCurrentComplicationUserInfo()在需要时从手机发送到手表。然而,如果手表仍然控制并拉取信息,则使用带有sendMessage:replyHandler:errorHandler:的WCSession向手机请求信息。可以使用replyhandler调用reloadTimelineForComplication或不使用处理程序并返回transferCurrentCOmplicationUserInfo()。祝好运! - El Tea
2
请注意,当调用requestedUpdateBudgetExhausted时,您可以在该调用中进行一次更新。这就像一个标记,再做一次,然后您将不得不等待x时间,直到手表决定补充您的预算。 - Saren Inden
如何从watch 3应用程序启动iOS应用程序? - Dmitry

17

El Tea的回答详细介绍了如何更新watchOS 2的复杂情况。

在watchOS 3中,保持您的复杂情况最新的推荐方法是使用后台刷新应用程序任务。您可以使用一系列后台任务来安排处理您的应用程序扩展在后台唤醒时的情况:

  • 获取新数据

  • 一旦数据到达,更新您的模型,
  • 更新您的复杂表盘(通过重新加载或扩展时间轴)以显示模型中可用的新数据,最后
  • 更新您应用程序的Dock快照以在Dock上显示数据

这样做更加功能强大和能源高效,因为它不会使用任何您的复杂表盘每日执行预算来获取数据或更新模型。

它还避免了任何不建议的方法所带来的复杂性,这些方法试图在仅旨在立即响应请求的复杂表盘数据源中异步获取数据。

我在另一个答案中提供了更多信息,以及WWDC视频和示例代码的链接。

总结watchOS 3的更改

使用预定的后台任务代替getNextRequestedUpdateDateWithHandler()

在应用程序任务中重新加载或扩展您的时间轴,而不是在requestedUpdateDidBegin()内部进行。


这是一篇很棒的文章,但是 watchOS 3 还未对公众开放。 - Simon
请注意,目前似乎无法使用 WKWatchConnectivityRefreshBackgroundTask,请参见此处。即使在苹果的演示项目 QuickSwitch 中也无法正常工作。 - Reinhard Männer
有没有办法在没有 iPhone 或服务器的情况下使用 watchOS 3 的新功能?如果我们想要 watchOS 安排后台任务怎么办?如何在不使用 WKWatchConnectivityRefreshBackgroundTask 或 WKURLSessionRefreshBackgroundTask 的情况下进行操作?假设模型数据在手表上独立更新,而不是与 iPhone 配对。你该如何从 watchOS 方法直接触发并安排后台任务,而无需了解已配对手机的信息? - Erika Electra
非常正确。实际上,在watchOS 4中,使用后台刷新应用程序任务是强制性的,而不是以前的requestedUpdateDidBegin()方式。你能否提供一个示例,展示如何将后台刷新应用程序任务实现到bkwebhero的代码中? - Allister Bah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接