Работа с HealthKit. Часть 2

Конец и начало года у разработчиков, как правило, всегда наполнены большим количеством проектов. Разобравшись со срочными делами и закатав повыше рукава, мы в Techmas пообещали себе делиться еще бОльшим количеством своих разработок с коллегами.

Несмотря на растущую популярность приложений, которые уже используют HealthKit, спрос на мобильные решения для оценки состояния здоровья продолжает развиваться.

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

06c165defe0248c6a48948eebe94823c.jpg


Введение


Напомним, ранее мы сделали обзор общего функционала HealthKit, рассмотрели приложение для аутентификации в Health и вывода характеристик (characteristics) пользователя. Сейчас мы разберемся с выборками (samples) для чтения и записи новых данных по тренировкам.

Выборки (samples) отличаются от характеристик (characteristics) тем, что они меняются с течением времени и зависят от поведения пользователя. Например, выборки используются для добавления новых данных по тренировкам, их визуализации, определения и расчета дополнительных параметров.

В примере мы покажем как считывать данные по росту и весу пользователя из приложения Health. Далее по ним рассчитаем показатель BMI (индекс массы тела) и добавим полученное значение для отслеживания динамики его изменения.

d4506f7805f3447eb89d5c2cce2a1da1.jpg

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

Подготовка выборки


Создадим тестовые данные для выборки в приложение Health.

Для этого выберем на закладке Body Measurements пункт Weight и внесем несколько ключевых точек с информацией о весе (weight) через Add Data Point.

55f2216061ae46ef8e29faefd5c3f581.jpeg

Данные можно вводить какие угодно и сколько угодно за разные промежутки времени.

Теперь получим разрешение работы с параметрами выборки. Добавим в функцию авторизации authorizeHealthKit () (отдельно мы говорили об авторизации ранее):

let healthKitTypesToWrite = Set(arrayLiteral:
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!,
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!
)


Чтение выборки из Health


В нашем примере создадим функцию readMostRecentSample (), которая будет считывать показатели по дате и типу.

Для получения выборки необходимо использовать подкласс HKSampleQuery, который наследуется от HKQuery. По сути, он работает аналогично NSFetchedRequest в Core Data.

При формировании запроса доступны параметры:
1. Тип выборки (например, вес или рост), Type.
2. Условия выделения выборки (период, значения, пр.), NSPredicate.
3. Метод сортировки, NSSortDescriptors.

Второй и третий параметры задаются опционально. После формирования запроса необходимо выполнить executeQuery () для вывода его результата.

В нашем примере создадим метод получения последнего значения для указанного типа выборки. Добавим следующий код в функцию readMostRecentSample ():

// 1. Подготовим параметры периода
let past = NSDate.distantPast()
let now = NSDate()
let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)

// 2. Зададим значение сортировки в обратном порядке 
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)

// 3. Ограничим вывод одним значением
let limit = 1

// 4. Выполним построение запроса
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
    { (sampleQuery, results, error ) -> Void in
        
        if let queryError = error {
            completion(nil,error)
            return;
        }
        
        // Выделим крайнее значение
        let mostRecentSample = results!.first as? HKQuantitySample
        
        if completion != nil {
            completion(mostRecentSample,nil)
        }
}

// 5. Выполним сам запрос
self.healthKitStore.executeQuery(sampleQuery)


Наша функция позволяет получать последние данные по выборкам определенного типа. Для простоты, вызов readMostRecentSample () добавим в метод viewDidLoad ():

// 1. Подготовка параметра HKSampleType для определения веса
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)

// 2. Вызов функции для выделения крайнего значения
self.readMostRecentSample(sampleType!, completion: { (mostRecentWeight, error) -> Void in
    
    if( error != nil )
    {
        print("Error reading weight from HealthKit Store: \(error.localizedDescription)")
        return;
    }


Здесь мы используем quantityTypeForIdentifier для определения необходимой выборки, в нашем случае это HKQuantityTypeIdentifierBodyMass.

Расчет значения BMI


Теперь создадим набор для расчета и записи BMI. Он будет рассчитываться по формуле:

bmi = weight / height ^ 2


Как было показано выше, выделяем значение веса (weight) из Health. Таким же образом мы получим рост (height) пользователя (заменив тип данных и формат, используем HKQuantityTypeIdentifierHeight).

Здесь нам понадобится объект HKQuantitySample. Также необходимо указать:

• Тип объекта HKQuantityType для сохранения. В нашем случае это HKQuantityTypeIdentifierBodyMassIndex.
• Объект HKQuantity. Он инициализируется с передаваемым значением из параметра bmi. Важно, что для преобразования скалярного типа Double используется HKUnit.countUnit (), который позволяет конвертировать типы в необходимые единицы измерения.

Тогда расчет BMI будет выглядеть следующим образом:

var weight: HKQuantitySample
var weightLocalizedString = "empty"

weight = (mostRecentWeight as? HKQuantitySample)!;
kilograms = weight.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo))
let weightFormatter = NSMassFormatter()
weightFormatter.forPersonMassUse = true;
weightLocalizedString = weightFormatter.stringFromKilograms(kilograms)

print(weightLocalizedString)

let weightInKilograms = kilograms
let heightInMeters: Double = 180

let bmi  = weightInKilograms / heightInMeters * heightInMeters

print(String(format: "%.02f", bmi))

Также отметим использование форматирование результатов с помощью NSMassFormatter. Сам NSMassFormater, в сущности, не является частью HelathKit, но позволяет работать с типами данных Health. В частности, если в приложение используются килограммы, а устройство не использует метрическую систему, мы имеем возможность привести единицы измерения к нужному виду.

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

Добавление новой выборки в Health

Наконец, для записи полученного значения BMI в HelathKit создадим метод saveBMISample (bmi: Double, date: NSDate). Выходные параметры: само значение BMI и дата его регистрации.

// 1. Создадим выборку BMI
let bmiType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)
let bmiQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: bmi)
let bmiSample = HKQuantitySample(type: bmiType, quantity: bmiQuantity, startDate: date, endDate: date)

// 2. Сохраним выборку в Health
healthKitStore.saveObject(bmiSample, withCompletion: { (success, error) -> Void in
if( error != nil ) {
  println("Error saving BMI sample: \(error.localizedDescription)")
} else {
  println("BMI sample saved successfully!")
}
})


Новое значение BMI доступно как для стандартного просмотра, так и может быть использовано сторонним авторизованным приложением.

Примечание


На практике все методы для работы c HealthKit имеет смысл вынести в отдельный класс.
Для нашего примера мы ограничились лишь демонстрацией ключевых этапов работы с платформой.

Код конечного приложения находится здесь.

© Habrahabr.ru