Каков процесс обновления данных о сложности для Apple Watch?

Я слежу за множеством руководств в Интернете, чтобы узнать, как настроить усложнение. У меня нет проблем с настройкой усложнения, как и ожидалось.

До истечения срока действия первоначальных записей на временной шкале. Через 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))
}

Здесь у меня проблемы.

Как мне создать новые данные и перезагрузить временную шкалу? Какой поток? Я не пытаюсь продлить сроки, а скорее полностью заменить их. Я в полной растерянности. документация Apple по этому поводу довольно расплывчата. Я знаю, что мне нужно реализовать следующие методы, но не знаю как. Может кто-нибудь помочь мне заполнить этот код?

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 у меня все заработало. Мне нужно добавить экземпляр CLKComplicationServer в requestUpdateDidBegin и поместить внутрь метод 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")
}

person bkSwifty    schedule 06.10.2015    source источник
comment
Вы когда-нибудь сталкивались с проблемой на стороне ComplicationController, когда кажется, что вы не можете получить данные, которые вы установили в ExtensionDelegate через WCSession:, для извлечения? В основном в том коде, который есть в вашей createData() функции, если он извлекается из ExtensionDelegate через let myDelegate = WKExtension.sharedExtension().delegate as! ExtensionDelegate, но фактически не извлекает какие-либо из этих данных. Я застрял на этом здесь: stackoverflow.com/questions/35542729/   -  person SRMR    schedule 22.02.2016


Ответы (2)


Последовательность обновления усложнения, выполняемого во временном интервале, выполняется следующим образом:

  • iOS вызывает вашу функцию requestedUpdateDidBegin() или requestedUpdateBudgetExhausted() (если ваш бюджет исчерпан, ничего из того, что вы делаете, не вызовет обновления, пока вам не будет предоставлено больше времени для выполнения.)
  • Внутри requestedUpdateDidBegin() вы должны вызвать reloadTimelineForComplication() или extendTimelineForComplication(), чтобы указать, какие из ваших усложнений вы хотите перезагрузить или добавить данные. Если вы этого не сделаете, ничего не произойдет!
  • В зависимости от того, позвонили ли вы reload или extend, iOS выполняет вызовы одного или обоих из getCurrentTimelineEntryForComplication() и getTimelineEntriesForComplication().
  • Независимо от того, обновили ли вы свое усложнение или нет, iOS звонит getNextRequestedUpdateDateWithHandler(), чтобы узнать, когда вы в следующий раз хотите повторить указанные выше шаги.

Примечание: последние два шага не обязательно должны выполняться в таком порядке.

Процесс работает таким образом, что iOS не просит вас повторно создавать одни и те же данные. Это дает вам возможность в requestedUpdateDidBegin() решить, нуждается ли ваше усложнение в обновлении. Если этого не произойдет, ваш код должен просто вернуться. (Это сокращает время выполнения вашего усложнения и помогает избежать отключения iOS вашего приложения от дальнейших обновлений из-за использования его дневного бюджета). Но если у вас есть новые данные, вам нужно сообщить об этом iOS, позвонив reloadTimelineForComplication() или extendTimelineForComplication()

Насколько я могу судить, все, что вы там написали, выглядит хорошо, за исключением того, что вы не запрашивали перезагрузку или расширение внутри 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)
    }
}

Обратите внимание, что помимо временных интервалов есть и другие способы инициировать обновления, включая push-оповещения, выполнение перезагрузок при запуске приложения для часов или использование инфраструктуры Watch Connectivity с WCSession, чтобы ваше телефонное приложение отправляло данные обновления для немедленного отображения через transferCurrentComplicationUserInfo(). См. Обновление данных о ваших осложнениях в документации Apple для получения дополнительной информации.

Я добился успеха в тестировании на симуляторе с интервалом обновления всего десять минут. Вероятно, вам не следует часто обновлять это на реальных часах из-за бюджета времени выполнения, но это позволит вам протестировать свой код, не дожидаясь 12 часов.

person El Tea    schedule 07.10.2015
comment
Я пробую! Должно быть, я не включил CLKComplicationServer. Я дам вам знать, если это сработает. - person bkSwifty; 07.10.2015
comment
Большой! Осложнения просты, но информации о них на данный момент катастрофически не хватает. Рад, что он у вас запущен. - person El Tea; 07.10.2015
comment
Все это отлично работает, если ваша функция createData () синхронна, но как насчет того, чтобы получить данные из приложения iOS? Есть ли лучший подход к вызову приложения iOS transferCurrentComplicationUserInfo? Хакерский альтернативный подход заключался бы в том, чтобы requestUpdateDidBegin вызвать reloadTimelineForComplication по таймеру с задержкой. - person RealCasually; 10.02.2016
comment
Не проверено, но мой подход будет зависеть от того, как генерируются данные в приложении iOS. Если данные на телефоне генерируются сами по себе, в своем собственном темпе, я думаю, что я бы просто использовал transferCurrentComplicationUserInfo(), как вы сказали, для отправки с телефона на часы, когда это необходимо. Однако, если часы остаются под контролем и получают информацию, используйте WCSession с sendMessage:replyHandler:errorHandler:, чтобы запросить информацию у телефона. Либо используйте обработчик ответа для вызова reloadTimelineForComplication, либо не используйте обработчик и вернитесь к transferCurrentCOmplicationUserInfo(). Удачи! - person El Tea; 10.02.2016
comment
Обратите внимание, что при вызове requestUpdateBudgetExhausted вам разрешено выполнить еще одно обновление в этом вызове. Это как маркер, похожий на еще раз, и тогда вам придется подождать x сумму, пока часы не решат пополнить ваш бюджет. - person Saren Inden; 20.04.2016
comment
Как можно запустить приложение iOS из приложения Watch 3? - person Dmitry; 11.11.2016

Ответ El Tea подробно описывает, как обновить усложнение для watchOS 2.

В watchOS 3 рекомендуемый способ обновления вашего усложнения заключается в использовании задач приложения фонового обновления < / а>. Вы можете использовать серию фоновых задач для расписания и обрабатывать активацию расширения вашего приложения в фоновом режиме, чтобы:

  • Получить новые данные

  • обновите вашу модель после поступления данных,
  • обновите свое усложнение (перезагрузив или расширив временную шкалу), чтобы отобразить новые данные, доступные из модели, и, наконец,
  • обновите снимок док-станции вашего приложения, чтобы отобразить данные в док-станции

Это гораздо более функционально, а также энергоэффективно, поскольку оно не использует дневной бюджет выполнения вашего усложнения для выборки данных или обновления модели.

Это также позволяет избежать каких-либо сложностей, связанных с нерекомендованными подходами, которые пытаются асинхронно извлекать данные из усложняющего источника данных, который предназначен только для немедленного ответа на запросы.

Я предоставил дополнительную информацию, а также ссылки на видео WWDC и образец кода, в другом ответе.

Подводя итоги изменений для watchOS 3

Используйте запланированные фоновые задачи вместо getNextRequestedUpdateDateWithHandler().

Перезагрузите или увеличьте временную шкалу в задаче приложения, а не в requestedUpdateDidBegin().

person Community    schedule 16.06.2016
comment
это отличное описание, но watchOS 3 недоступна для широкой публики. - person Simon; 22.06.2016
comment
Имейте в виду, что использование WKWatchConnectivityRefreshBackgroundTask в настоящее время кажется невозможным, см. здесь . Он не работает даже в демонстрационном проекте Apple QuickSwitch . - person Reinhard Männer; 16.11.2016
comment
Есть ли способ использовать новые функции watchOS 3 без iPhone или сервера? Что, если мы хотим, чтобы watchOS планировала фоновые задачи? Как это сделать без использования WKWatchConnectivityRefreshBackgroundTask или WKURLSessionRefreshBackgroundTask? Допустим, данные модели на часах постоянно обновляются независимо от подключенного iPhone. Как бы вы инициировали это, а затем запланировали бы фоновую задачу непосредственно из методов watchOS, не зная о сопряженном телефоне? - person Erika Electra; 09.02.2017
comment
Совершенно верно. Фактически, в watchOS 4 использование задач приложения фонового обновления является обязательным по сравнению со старым способом requiredUpdateDidBegin (). Не могли бы вы опубликовать пример того, как реализовать задачи фонового обновления приложения в коде bkwebhero? - person Allister Bah; 23.06.2018