Работа с HealthKit. Часть 2
Конец и начало года у разработчиков, как правило, всегда наполнены большим количеством проектов. Разобравшись со срочными делами и закатав повыше рукава, мы в Techmas пообещали себе делиться еще бОльшим количеством своих разработок с коллегами.
Несмотря на растущую популярность приложений, которые уже используют HealthKit, спрос на мобильные решения для оценки состояния здоровья продолжает развиваться.
Для пояснения всех основных принципов работы c платформой, мы решили вернуться к примеру из прошлой публикации и дополнить его работой с выборками данных из Health.
Введение
Напомним, ранее мы сделали обзор общего функционала HealthKit, рассмотрели приложение для аутентификации в Health и вывода характеристик (characteristics) пользователя. Сейчас мы разберемся с выборками (samples) для чтения и записи новых данных по тренировкам.
Выборки (samples) отличаются от характеристик (characteristics) тем, что они меняются с течением времени и зависят от поведения пользователя. Например, выборки используются для добавления новых данных по тренировкам, их визуализации, определения и расчета дополнительных параметров.
В примере мы покажем как считывать данные по росту и весу пользователя из приложения Health. Далее по ним рассчитаем показатель BMI (индекс массы тела) и добавим полученное значение для отслеживания динамики его изменения.
Итак, вернемся к приложению из предыдущей части. Его код находится здесь.
Подготовка выборки
Создадим тестовые данные для выборки в приложение Health.
Для этого выберем на закладке Body Measurements пункт Weight и внесем несколько ключевых точек с информацией о весе (weight) через Add Data Point.
Данные можно вводить какие угодно и сколько угодно за разные промежутки времени.
Теперь получим разрешение работы с параметрами выборки. Добавим в функцию авторизации 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 имеет смысл вынести в отдельный класс.
Для нашего примера мы ограничились лишь демонстрацией ключевых этапов работы с платформой.
Код конечного приложения находится здесь.