Как можно отобразить данные HealthKit для усложнения, которое обновляется в фоновом режиме?

Я пытаюсь создать усложнение watchOS 2, которое отображает данные о здоровье пользователя, такие как шаги (но теоретически оно должно иметь возможность отображать любые данные о здоровье, которые пользователь предоставил приложению для просмотра). Когда усложнение запускается впервые, я могу запросить Healthkit и получить все данные, которые мне нужны, потому что первый запуск считается на переднем плане. Однако у меня возникают проблемы с получением данных HealthKit в фоновом режиме, когда доступны новые данные о работоспособности. Есть два места, где я могу получить эти данные: часы и iPhone.

Я попытался получить данные от самих часов, когда фоновое обновление усложнения запускается с даты, установленной в getNextRequestedUpdateDateWithHandler. Однако, когда я вызываю метод execute HKHealthStore, он не возвращает никаких результатов запроса, если приложение (или, в данном случае, усложнение) работает в фоновом режиме. Я также попытался настроить запрос HKAnchoredObject, который должен немедленно возвращать мои результаты при возобновлении процесса, но это также, похоже, не возвращает никаких результатов, если я вручную не запущу расширение приложения на часах. Вот мой код наблюдения, вызываемый из метода инициализации моего ExtensionDelegate после запроса разрешений комплекта работоспособности:

func setupComplicationDataCache() {
    let now = NSDate()
    var startDate: NSDate? = nil
    var interval: NSTimeInterval = 0

    self.calendar.rangeOfUnit(NSCalendarUnit.Day, startDate: &startDate, interval: &interval, forDate: now)
    let stepSampleType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!

    // Match samples with a start date after the workout start
    let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: nil, options: .None)
    let query = HKAnchoredObjectQuery(type: stepSampleType, predicate: predicate, anchor: nil, limit: 0) { (query, samples, deletedObjects, anchor, error) -> Void in
        // Handle when the query first returns results
        self.handleStepResults(query, samples: samples, deletedObjects: deletedObjects, anchor: anchor, error: error)
    }

    query.updateHandler = { (query, samples, deletedObjects, anchor, error) -> Void in
        // Handle update notifications after the query has initially run
        self.handleStepResults(query, samples: samples, deletedObjects: deletedObjects, anchor: anchor, error: error)
    }

    self.healthStore.executeQuery(query);
}

func handleStepResults(query: HKAnchoredObjectQuery, samples: [HKSample]?, deletedObjects: [HKDeletedObject]?, anchor: HKQueryAnchor?, error: NSError?) {
    if error != nil {
        self.timelineModel.currentEntry = TimelineEntryModel(value: NSNumber(int: -1), endDate: NSDate())
    } else if samples == nil || samples?.count == 0 {
        self.timelineModel.currentEntry = TimelineEntryModel(value: NSNumber(int: 0), endDate: NSDate())
    } else {
        let newStepSamples = samples as! [HKQuantitySample]
        var stepCount = self.timelineModel.currentEntry.value.doubleValue
        var currentDate = self.timelineModel.currentEntry.endDate

        // Add the current entry to the collection of past entries
        self.timelineModel.pastEntries.append(self.timelineModel.currentEntry)

        // Add all new entries to the collection of past entries
        for result in newStepSamples {
            stepCount += result.quantity.doubleValueForUnit(self.countUnit)
            currentDate = result.endDate
            self.timelineModel.pastEntries.append(TimelineEntryModel(value: NSNumber(double: stepCount), endDate: currentDate))
        }

        // Retrieve the latest sample as the current item
        self.timelineModel.currentEntry = self.timelineModel.pastEntries.popLast()
        if self.timelineModel.currentEntry == nil {
            self.timelineModel.currentEntry = TimelineEntryModel(value: NSNumber(int: -3), endDate: NSDate())
        }
    }

    // Reload the complication
    let complicationServer = CLKComplicationServer.sharedInstance()
    for complication in complicationServer.activeComplications {
        complicationServer.reloadTimelineForComplication(complication)
    }
}

Я также пытался получить данные с iPhone с помощью HKObserverQuery. У меня есть запрос наблюдателя, который может разбудить iPhone раз в час (максимальное время для пошаговых данных). Однако, если iPhone заблокирован, когда обработчик завершения наблюдателя выполняет мой пошаговый запрос, метод выполнения HKHealthStore также отказывается возвращать какие-либо результаты запроса. Я думаю, что здесь это имеет смысл, и, вероятно, нет способа обойти это, потому что В документах Apple упоминается, что Health Store шифруется, когда устройство заблокировано и вы не можете читать из хранилища (только записывать). НО в случае с часами, когда они на чьем-то запястье, они не заблокированы, просто выключен экран.

Кто-нибудь знает, как заставить обновления HealthKit отображаться в усложнении, когда обновление происходит в фоновом режиме, либо в iOS, либо в watchOS 2?


person lehn0058    schedule 06.01.2016    source источник
comment
Какую скорость вы пытались использовать при обновлении усложнений на часах?   -  person Cobra    schedule 10.01.2016
comment
Это не имело значения. Я пробовал 30 минут и 1 час, оба из которых были в порядке без использования запросов HealthKit.   -  person lehn0058    schedule 10.01.2016


Ответы (1)


После обширного тестирования я определил, что в настоящее время это невозможно. В watchOS 2 Apple, похоже, полностью отключила запросы HealthKit, чтобы они не возвращали результаты, когда расширение или усложнение работает в фоновом режиме. Это включает в себя выполнение из удаленных уведомлений, Watch Connectivity и из запланированного обновления осложнений. Запросы HealthKit iPhone завершаются ошибкой, если экран выключен, а на устройстве установлен пароль. Запросы не выполняются, поскольку хранилище данных о работоспособности шифруется, когда устройство заблокировано. Запросы не выполняются, даже если включены запросы наблюдателя и фоновая доставка. Вы можете получать уведомления о том, что что-то изменилось, но вы не можете запросить изменения, пока iPhone не будет разблокирован (поскольку данные опять же зашифрованы).

Другие приложения, которые показывают данные, связанные с набором для здоровья, такие как шаги и расстояние ходьбы + бега, делают это, напрямую запрашивая шагомер (CMPedometer), данные которого доступны в этих фоновых режимах.

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

person lehn0058    schedule 07.01.2016