[Перевод] Знакомство с HealthKit

В этой статье про HealthKit вы узнаете, как запрашивать разрешение на доступ к данным HealthKit, а также считывать и записывать данные в центральный репозиторий HealthKit. В статье используется Swift 4, iOS 11, Xcode 9 версии.

HealthKit — это API, которое было представлено в iOS 8. HealthKit служит как центральный репозиторий для всех данных, связанных со здоровьем, позволяя пользователям создавать биологический профиль и хранить данные о тренировках.

По ходу прочтения статьи о HealthKit, вы создадите простейшее приложение для отслеживания тренировок и узнаете:

  • Как запросить разрешение и получить доступ к данным HealthKit
  • Как читать данные HealthKit и отображать их в UITableView
  • Как записать данные в центральный репозиторий HealthKit


Готовы начать знакомство с HealthKit? Читайте дальше!
Примечание: Для работы по этому туториалу вам потребуется активная учетная запись iOS разработчика. Без этого вы не сможете включить HealthKit Capability и получить доступ к хранилищу HealthKit.

Начало


Стартовое приложения отслеживает сжигание калорий при выполнении программы тренировки. Для голливудских инсайдеров и светских людей должно быть очевидно, что я говорю о Prancercise.

Prancercise


Загрузите стартовый проект и откройте его в Xcode.

Скомпилируйте и запустите приложение. Вы увидите «скелет» пользовательского интерфейса. В течении следующих двух статей вы постепенно будете добавлять функционал для этого приложения.

image


Assigning a Team


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

Выберите PrancerciseTracker в Навигаторе проектов, а затем выберите PrancerciseTracker target. Перейдите на вкладку General и нажмите на поле Team.

Выберите команду, связанную с вашей учетной записью разработчика:

image


Разрешения/Entitlements


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

Откройте вкладку Capabilities в target редакторе и включите HealthKit, как показано на скриншоте ниже:

image

Подождите, пока Xcode настроит HealthKit для вас. Как правило, здесь нет проблем, но с некоторыми вы все же можете столкнуться, если забудете указать правильно Team и Bundle Identifier.

Теперь все готово. Вам просто нужно запросить у пользователя разрешение на использование HealthKit.

Права доступа/Permissions


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

Вот почему HealthKit обладает надежной системой конфиденциальности. HealthKit имеет доступ только к тем данным, которыми пользователи согласны поделиться. Чтобы создать профиль для пользователей вашего Prancercise Tracker, вам необходимо сначала получить разрешение на доступ к каждому типу данных.

Обновление Описания использования


Во-первых, вам нужно описать, почему вы запрашиваете показатели здоровья у своих пользователей. Xcode дает вам возможность указать это в файле Info.plist вашего приложения.

Откройте Info.plist. Затем добавьте следующие ключи:
Privacy — Health Share Usage Description
Privacy — Health Update Usage Description

Эти ключи хранят текст, который будет отображаться при появлении экрана авторизации HeathKit. Health Share Usage Description относится к разделу данных, которые следует читать из HealthKit. Health Update Usage Description соответствует данным, которые записываются в HealthKit.

Вы можете «добавить» туда все, что захотите. Обычно это описание: «Мы будем использовать вашу информацию о здоровье, чтобы лучше отслеживать тренировки».

Имейте в виду, что если эти ключи не установлены, при попытке авторизации в HealthKit приложение «упадет».

Авторизация HealthKit


Откройте файл HealthKitSetupAssistant.swift внутри которого находится метод класса, который вы будете использовать для авторизации в HealthKit.

class func authorizeHealthKit(completion: @escaping (Bool, Error?) -> Swift.Void) {   
}


Метод authorizeHealthKit (completion:) не принимает никаких параметров, и имеет complition, который возвращает логическое значение (success или failure) и опциональную ошибку в случае, если что-то пойдет не так. Если возвращаются ошибки, вы передадите их в complition в двух случаях:

  1. HealthKit может быть недоступен на устройстве. Например если приложение запущено на iPad.
  2. Некоторые типы данных могут быть недоступны в текущей версии HealthKit.


Давайте нарушим этот процесс. Чтобы авторизовать HealthKit, метод authorizeHealthKit (completion:) должен выполнить следующие четыре действия:

  1. Проверить, доступен ли Healthkit на этом устройстве. Если это не так, вернуть в complition сбой и ошибку.
  2. Подготовить типы данных о состоянии здоровья. Prancercise Tracker будет читать и записывать в HealthKit.
  3. Организуйте эти данные в список типов, которые нужно считывать, и типы, которые нужно записать.
  4. Запросить авторизацию. Если это действие будет успешным, значит все активности были правильными и завершились надлежащим образом.


Проверка доступности HealthKit


Прежде всего нужно проверить наличие HealthKit на устройстве.
Вставьте следующий код в начало метода authorizeHealthKit (completion:):

//1. Проверьте, доступен ли на этом устройстве HealthKit
guard HKHealthStore.isHealthDataAvailable() else {
  completion(false, HealthkitSetupError.notAvailableOnDevice)
  return
}


Вы будете очень часто взаимодействовать с HKHealthStore. Он представляет собой центральный репозиторий, в котором хранятся данные о здоровье пользователя. Метод isHealthDataAvailable () поможет вам понять, поддерживает ли текущее устройство пользователя данные c Heathkit.

Оператор guard запрещает приложению выполнять остальную часть метода authorizeHealthKit (completion:), если HealthKit недоступен на устройстве. Когда это произойдет, вызовется completion блок с ошибкой notAvailableOnDevice. Вы можете просто вывести это в консоль или в главном контроллере обработать дальнейшие шаги в случае такой ошибки.

Подготовка данных


Как только вы узнаете, что HealthKit доступен на устройстве пользователя, пришло время подготовить типы данных, которые будут считываться и записываться в HealthKit.
HealthKit работает с типом HKObjectType. Каждый тип, который входит в центральный репозиторий HealthKit или возвращается, является своего рода HKObjectType. Вы также увидите HKSampleType и HKWorkoutType. Оба они наследуються от HKObjectType, поэтому в основном это одно и то же.

Вставьте следующий фрагмент кода сразу после первого фрагмента кода:

//2. Подготовка типы данных, которые будут взаимодействовать с HealthKit
guard let dateOfBirth = HKObjectType.characteristicType(forIdentifier: .dateOfBirth),
        let bloodType = HKObjectType.characteristicType(forIdentifier: .bloodType),
        let biologicalSex = HKObjectType.characteristicType(forIdentifier: .biologicalSex),
        let bodyMassIndex = HKObjectType.quantityType(forIdentifier: .bodyMassIndex),
        let height = HKObjectType.quantityType(forIdentifier: .height),
        let bodyMass = HKObjectType.quantityType(forIdentifier: .bodyMass),
        let activeEnergy = HKObjectType.quantityType(forIdentifier: .activeEnergyBurned) else {
        
        completion(false, HealthkitSetupError.dataTypeNotAvailable)
        return
}


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

Чтобы создать HKObjectType для данных характеристик, вам нужно использовать либо HKObjectType.characteristicType (forIdentifier:), либо HKObjectType.quantityType (forIdentifier:)
Типы характеристик и типы количества представляют собой перечисления, определенные фреймворком. HealthKit загружается вместе с ними.

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

Подготовка списка типов данных для чтения и записи


Теперь пришло время подготовить список типов данных для чтения и записи.
Вставьте этот третий код в метод authorizeHealthKit (completion:) сразу после второй части:

//3. Подготовьте список типов, которые HealthKit должен считывать и записывать
let healthKitTypesToWrite: Set = [bodyMassIndex, activeEnergy, HKObjectType.workoutType()]
    
let healthKitTypesToRead: Set = [dateOfBirth,
                                                                         bloodType,
                                                                         biologicalSex,
                                                                         bodyMassIndex,
                                                                         height,
                                                                         bodyMass,
                                                                         HKObjectType.workoutType()]


HealthKit ожидает набор объектов HKSampleType, которые представляют виды данных, которые может написать ваш пользователь, а также ожидает, что для вашего приложения будет отображаться набор объектов HKObjectType.

HKObjectType.workoutType () — особый тип HKObjectType. Он представляет собой любую тренировку.

Авторизация HealthKit


Последняя часть — самая легкая. Вам просто нужно запросить авторизацию от HealthKit. Вставьте этот последний фрагмент кода:

//4. Запрос на авторизацию
HKHealthStore().requestAuthorization(toShare: healthKitTypesToWrite,
                                     read: healthKitTypesToRead) { (success, error) in
  completion(success, error)
}


Этот код запрашивает авторизацию от HealthKit, а затем вызывает completion. Они используют переменные успешного выполнения операций и ошибок, переданных из метода HKHealthStore«s requestAuthorization (toShare: read: completion:).

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

В проекте уже есть кнопка Authorize HealthKit, и она вызывает метод authorizeHealthKit () в MasterViewController. Это идеальное место для вызова метода авторизации, который мы только что написали.

Откройте MasterViewController.swift, найдите метод authorizeHealthKit () и вставьте этот код:

HealthKitSetupAssistant.authorizeHealthKit { (authorized, error) in
  guard authorized else {
    let baseMessage = "HealthKit Authorization Failed"
        
    if let error = error {
      print("\(baseMessage). Reason: \(error.localizedDescription)")
    } else {
      print(baseMessage)
    }
    return
  }
  print("HealthKit Successfully Authorized.")
}


Этот код использует метод authorizeHealthKit (completion:), который вы только что внедрили. Когда он будет завершен, он выведет сообщение в консоль, чтобы сообщить, была ли авторизация успешной в HealthKit.

Запустите приложение. Нажмите Authorize HealthKit в главном окне, и вы увидите всплывающий экран авторизации:

image


Включите все переключатели, прокрутите экран, чтобы просмотреть их все, и нажмите Allow. В консоли вы должны увидеть такое сообщение:

HealthKit Successfully Authorized.


Великолепно! Приложение имеет доступ к центральному хранилищу HealthKit. Теперь пришло время начать отслеживать элементы.

Характеристики и сэмплы


В этом разделе вы узнаете:

  • Как читать биологические характеристики вашего пользователя.
  • Как считать и записать разные типы образцов (вес, высота и т. д.)


Биологические характеристики, как правило, являются видами элементов, которые не меняются, как собственно ваша группа крови. Сэмплы представляют собой элементы, которые часто меняются, например, вес.

Чтобы правильно отслеживать эффективность режима тренировки Prancercise, приложение Prancercise Tracker должно получить сэмпл веса и роста пользователя. В совокупности эти сэмплы можно использовать для расчета индекса массы тела (ИМТ).

Примечание: Индекс массы тела (ИМТ) является широко используемым показателем жира в организме, и он рассчитывается на основе веса и роста человека. Узнайте больше об этом здесь.

Чтение характеристик


Приложение Prancercise Tracker не записывает биологические характеристики. Он получает их из HealthKit. Это означает, что эти характеристики необходимо сначала сохранить в центральном хранилище HeathKit.

Если вы еще этого не сделали, пришло время рассказать для HeathKit немного больше о себе.

Откройте приложение «Здоровье» на своем устройстве или в симуляторе. Выберите вкладку «Данные о здоровье». Затем нажмите значок профиля в верхнем правом углу, чтобы просмотреть свой профиль здоровья. Нажмите Edit и введите информацию дату рождения, пол, группу крови:

image


Теперь, когда HealthKit знает вашу дату рождения, пол и группу крови, пришло время прочитать эти характеристики в Prancercise Tracker.

Вернитесь к Xcode и откройте ProfileDataStore.swift. Класс ProfileDataStore представляет вашу точку доступа ко всем связанным со здоровьем данным для ваших пользователей.

Вставьте следующий метод в ProfileDataStore:

class func getAgeSexAndBloodType() throws -> (age: Int, 
                                              biologicalSex: HKBiologicalSex, 
                                              bloodType: HKBloodType) {
    
  let healthKitStore = HKHealthStore()
  do {
    //1. This method throws an error if these data are not available.
    let birthdayComponents =  try healthKitStore.dateOfBirthComponents()
    let biologicalSex =       try healthKitStore.biologicalSex()
    let bloodType =           try healthKitStore.bloodType()
      
    //2. Use Calendar to calculate age.
    let today = Date()
    let calendar = Calendar.current
    let todayDateComponents = calendar.dateComponents([.year], from: today)
    let thisYear = todayDateComponents.year!
    let age = thisYear - birthdayComponents.year!
     
    //3. Unwrap the wrappers to get the underlying enum values. 
    let unwrappedBiologicalSex = biologicalSex.biologicalSex
    let unwrappedBloodType = bloodType.bloodType
      
    return (age, unwrappedBiologicalSex, unwrappedBloodType)
  }
}


Метод getAgeSexAndBloodType () обращается к HKHealthStore, запрашивает дату рождения пользователя, пол и группу крови. Он также вычисляет возраст пользователя, используя дату рождения.

  1. Возможно, вы заметили, что этот метод может вызвать ошибку. Это происходит всякий раз, когда дата рождения, пол или группа крови не были сохранены в центральном хранилище HealthKit. Поскольку вы только что ввели эту информацию в свое приложение, не следует вызывать ошибки.
  2. Используя класс Calendar, вы можете преобразовать любую дату в набор Date Components. Это действительно удобно, когда вы хотите получить год для даты. Этот код просто получает ваш год рождения, текущий год, а затем вычисляет разницу.
  3. «Развернутые» переменные называются таким образом, чтобы было ясно, что вам нужно получить доступ к базовому перечислению из wrapper class (HKBiologicalSexObject и HKBloodTypeObject).


Обновление пользовательского интерфейса


Если бы вы сейчас скомпилируете и зупустите приложение, вы не увидели никаких изменений в пользовательском интерфейсе, потому что вы еще не подключили к нему эту логику.
Откройте ProfileViewController.swift и найдите метод loadAndDisplayAgeSexAndBloodType ()

Этот метод будет использовать ваш ProfileDataStore для загрузки биологических характеристик в пользовательский интерфейс.

Вставьте следующий код в метод loadAndDisplayAgeSexAndBloodType ():

do {
  let userAgeSexAndBloodType = try ProfileDataStore.getAgeSexAndBloodType()
  userHealthProfile.age = userAgeSexAndBloodType.age
  userHealthProfile.biologicalSex = userAgeSexAndBloodType.biologicalSex
  userHealthProfile.bloodType = userAgeSexAndBloodType.bloodType
  updateLabels()
} catch let error {
  self.displayAlert(for: error)
}


Этот блок кода загружает возраст, пол и группу крови в виде кортежа. Затем он устанавливает эти поля в локальном экземпляре модели UserHealthProfile. Наконец, он обновляет пользовательский интерфейс с новыми полями в UserHealthProfile, вызывая метод updateLabels ().

Поскольку метод ProfileDataStore getAgeSexAndBloodType () может выбросить ошибку, ProfileViewController должен обрабатывать ее. В этом случае вы просто берете ошибку и представляете ее как предупреждение.

Все это замечательно, но есть один улов. Метод updateLabels () еще ничего не делает. Это просто пустое объявление. На этот раз перейдем к пользовательскому интерфейсу.

Найдите метод updateLabels () и вставьте в него этот код:

if let age = userHealthProfile.age {
  ageLabel.text = "\(age)"
}

if let biologicalSex = userHealthProfile.biologicalSex {
  biologicalSexLabel.text = biologicalSex.stringRepresentation
}

if let bloodType = userHealthProfile.bloodType {
  bloodTypeLabel.text = bloodType.stringRepresentation
}


Код довольно прост. Если пользователь установил возраст, он будет отформатирован в метку. То же самое касается биологического пола и группы крови. Переменная stringRepresentation преобразует перечисление в строку для целей отображения.

Скомпилируйте и запустите приложение. Перейдите на экран Profile&BMI. Нажмите кнопку Read HealthKit Data.

image


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

Здорово! Вы считываете и отображаете данные непосредственно из HealthKit.

Сэмплы запросов


Теперь пришло время прочитать вес и рост пользователя. Они будут использоваться для расчета и отображения ИМТ в представлении профиля.

Биологические характеристики легкодоступны, потому что они почти никогда не меняются. Сэмплы требуют гораздо более сложного подхода. Они используют HKQuery, точнее HKSampleQuery.

Чтобы запросить сэмплы из HealthKit, вам понадобятся:

  1. Указать тип сэмпла, который вы хотите запросить (вес, высота и т. д.),
  2. Некоторые дополнительные параметры, которые помогают фильтровать и сортировать данные. Для этого вы можете передать опционал NSPredicate или массив NSSortDescriptors.


Примечание: Если вы знакомы с CoreData, вы, вероятно, заметили некоторые сходства. HKSampleQuery очень похож на NSFetchedRequest для типа объекта, где вы указываете предикат и дескрипторы сортировки, а затем задаете контекст объекта для выполнения запроса, чтобы получить результаты.

После того, как ваш запрос установлен, вы просто вызываете метод HKHealthStore ExecuteQuery () для получения результатов.

Для Prancercise Tracker вы создадите единую универсальную функцию, которая загружает самые последние сэмплы любого типа. Таким образом, вы можете использовать его как для веса, так и для роста.

Откройте ProfileDataStore.swift и вставьте следующий метод в класс, чуть ниже метода getAgeSexAndBloodType ():

class func getMostRecentSample(for sampleType: HKSampleType,
                               completion: @escaping (HKQuantitySample?, Error?) -> Swift.Void) {
  
//1. Используйте HKQuery для загрузки последних образцов.  
let mostRecentPredicate = HKQuery.predicateForSamples(withStart: Date.distantPast,
                                                      end: Date(),
                                                      options: .strictEndDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let limit = 1
let sampleQuery = HKSampleQuery(sampleType: sampleType,
                                predicate: mostRecentPredicate,
                                limit: limit,
                                sortDescriptors: [sortDescriptor]) { (query, samples, error) in
    
    //2. Всегда отправляйте в основной поток по завершении.
    DispatchQueue.main.async {    
      guard let samples = samples,
            let mostRecentSample = samples.first as? HKQuantitySample else {
            completion(nil, error)
            return
      }
      completion(mostRecentSample, nil)
    }
  }
HKHealthStore().execute(sampleQuery)
}


Этот метод использует тип выборки (высота, вес, ИМТ и т. д.). Затем он создает запрос для получения последнего сэмпла для этого типа. Если вы пройдете по типу сэмпла для роста, вы вернетесь к своей последней записи роста.

Здесь многое происходит. Я остановлюсь, чтобы объяснить несколько вещей.

  1. В HKQuery есть несколько методов, которые могут помочь вам отфильтровать ваши примеры запросов HealthKit. В этом случае мы используем встроенный предикат даты.
  2. Запрос образцов из HealthKit — это асинхронный процесс. Вот почему код в обработчике завершения встречается внутри блока Dispatch. Нужно, чтобы комплишн выполнялся на основном потоке. Если вы этого не сделаете, приложение выйдет из строя.


Если все будет хорошо, ваш запрос будет выполнен, и вы получите аккуратный сэмпл, возвращенный на main потоке, где ProfileViewController может поместить его содержимое в метку. Давайте сделаем эту часть сейчас.

Отображение образцов в пользовательском интерфейсе


В предыдущем разделе вы загрузили данные из HealthKit. Сохраните их в качестве модели в ProfileViewController, а затем обновите содержимое в строках с использованием метода ProfileViewController updateLabels ()

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

Откройте файл ProfileViewController.swift, найдите метод loadAndDisplayMostRecentHeight () и вставьте следующий код:

//1. Используйте HealthKit для создания типа сэмпла высоты
guard let heightSampleType = HKSampleType.quantityType(forIdentifier: .height) else {
  print("Height Sample Type is no longer available in HealthKit")
  return
}
    
ProfileDataStore.getMostRecentSample(for: heightSampleType) { (sample, error) in
  guard let sample = sample else {
    if let error = error {
      self.displayAlert(for: error)
    }   
    return
  }
      
   //2. Преобразуйте сэмпл высоты в метры, сохраните модель профиля,
  // и обновите пользовательский интерфейс.
  let heightInMeters = sample.quantity.doubleValue(for: HKUnit.meter())
  self.userHealthProfile.heightInMeters = heightInMeters
  self.updateLabels()
}


  1. Этот метод начинается с создания типа сэмпла роста. Затем он передает этот тип сэмпла методу, который вы только что написали, который вернет самый последний сэмпл роста пользователя, записанный в HealthKit.
  2. Как только сэмпл возвращается, рост преобразуется в метры и сохраняется в модель UserHealthProfile. Затем UI обновится.


Примечание: Обычно вы хотите преобразовать сэмпл количества в стандартную единицу. Для этого в приведенном выше коде используется метод doubleValue (for:), который позволяет вам передать в HKUnit соответствующие данные, которые вам необходимы (в данном случае метры).

Вы можете создавать различные типы HKUnits, используя некоторые распространенные методы класса, доступные через HealthKit. Чтобы получить счетчики, вы просто можете использовать метод meter () в HKUnit, и это будет то, что Вам необходимо.

С ростом разобрались. Как насчет веса? Все достаточно похоже, но вам нужно будет заполнить метода loadAndDisplayMostRecentWeight () в ProfileViewController.

Вставьте следующий код в метод loadAndDisplayMostRecentWeight () :

guard let weightSampleType = HKSampleType.quantityType(forIdentifier: .bodyMass) else {
  print("Body Mass Sample Type is no longer available in HealthKit")
  return
}
    
ProfileDataStore.getMostRecentSample(for: weightSampleType) { (sample, error) in
  guard let sample = sample else {
    if let error = error {
      self.displayAlert(for: error)
    }
    return
  }
      
  let weightInKilograms = sample.quantity.doubleValue(for: HKUnit.gramUnit(with: .kilo))
  self.userHealthProfile.weightInKilograms = weightInKilograms
  self.updateLabels()
}


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

На данный момент может показать что дело сделано, но есть кое-что еще. Функция updateLabels () не знает о новых данных, которые вы сделали доступными для нее.

Давайте это исправим.

Добавьте следующие строки в функцию updateLabels (), чуть ниже части, где вы разворачиваете группу крови, чтобы отобразить ее на пользовательском интерфейсе:

if let weight = userHealthProfile.weightInKilograms {
  let weightFormatter = MassFormatter()
  weightFormatter.isForPersonMassUse = true
  weightLabel.text = weightFormatter.string(fromKilograms: weight)
}
    
if let height = userHealthProfile.heightInMeters {
  let heightFormatter = LengthFormatter()
  heightFormatter.isForPersonHeightUse = true
  heightLabel.text = heightFormatter.string(fromMeters: height)
}
   
if let bodyMassIndex = userHealthProfile.bodyMassIndex {
  bodyMassIndexLabel.text = String(format: "%.02f", bodyMassIndex)
}


Следуя исходному шаблону в функцииupdateLabels (), она разворачивает индекс роста, веса и массы тела в вашей модели UserHealthProfile. Если они доступны, они генерируют соответствующие строки и присваивает их в labels на экране пользователя.

MassFormatter и LengthFormatter выполняют работу по преобразованию ваших величин в строки.

Индекс массы тела фактически не хранится в модели UserHealthProfile. Это вычисленное свойство, которое делает расчет для вас.

Выполните клик на свойстве bodyMassIndex, и вы увидите, что я имею в виду:

var bodyMassIndex: Double? {
  guard let weightInKilograms = weightInKilograms,
    let heightInMeters = heightInMeters,
    heightInMeters > 0 else {
    return nil
  }
  return (weightInKilograms/(heightInMeters*heightInMeters))
}


Индекс массы тела является не обязательным свойством, то есть он может возвращать nil, если не задали ни рост, ни вес (или если они установлены на некоторое число, которое не имеет никакого смысла). Фактический расчет — это просто вес, разделенный на рост в квадрате.

Примечание: Скоро вы погрузните во всем этом, если вы не добавили данные в HealthKit для считывания приложением. Если вы еще этого не сделали, вам нужно создать сэмплы роста и веса, по крайней мере.

Откройте приложение «Здоровье» и перейдите на вкладку «Данные о здоровье». Там выберите параметр Body Measurements, затем выберите Weight, а затем Add Data Point, чтобы добавить новый сэмпл веса. Повторите процесс для роста.

На этом этапе Prancercise Tracker должен иметь возможность прочитать недавний сэмпл веса и высоты вашего пользователя, а затем отобразить его в тексте.

Скомпилируйте и запустите приложения. Перейдите к Profile&BMI. Затем нажмите кнопку Read HealthKit Data.

image


Потрясающие! Вы только что прочитали свои первые сэмплы из хранилища HealthKit и использовали их для расчета ИМТ.

Сохранение сэмплов


У Prancercise Tracker уже есть удобный калькулятор индекса массы тела. Давайте использовать его для записи сэмпла ИМТ вашего пользователя.

Откройте ProfileDataStore.swift и добавьте следующий метод:

class func saveBodyMassIndexSample(bodyMassIndex: Double, date: Date) {
  
  //1.  Убедитесь, что тип массы тела существует   
  guard let bodyMassIndexType = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex) else {
    fatalError("Body Mass Index Type is no longer available in HealthKit")
  }
    
  //2.  Используйте граф HKUnit для создания массы тела
  let bodyMassQuantity = HKQuantity(unit: HKUnit.count(), doubleValue: bodyMassIndex)
  let bodyMassIndexSample = HKQuantitySample(type: bodyMassIndexType,
                                             quantity: bodyMassQuantity,
                                             start: date,
                                             end: date)
    
   //3.  Сохраните то же самое в HealthKit
  HKHealthStore().save(bodyMassIndexSample) { (success, error) in
    if let error = error {
      print("Error Saving BMI Sample: \(error.localizedDescription)")
    } else {
      print("Successfully saved BMI Sample")
    }
  }
}


Как и в случае с другими типами образцов, сначала необходимо убедиться, что тип сэмпла доступен в HealthKit.

  1. В этом случае код проверяет, существует ли quantity type для индекса массы тела. Если это так, то он используется для создания сэмпла количества. Если нет, приложение прекращает работу.
  2. Метод count () в HKUnit предназначен для специального случая, когда нет четкого блока для типа сэмпла, который вы храните. В какой-то момент в будущем может появится единица, присвоенная индексу массы тела, но на данный момент эта более общая единица работает очень хорошо.
  3. HKHealthStore сохраняет сэмпл и позволяет узнать, был ли процесс успешным из последующего замкнутого выражения. Вы можете сделать больше с этим, но теперь приложение просто отображает информацию на консоли.


Почти готово. Подведем итог по пользовательскому интерфейсу.

Откройте ProfileViewController.swif, найдите метод saveBodyMassIndexToHealthKit (). Этот метод вызывается, когда пользователь нажимает на кнопку Save BMI в таблице.

Вставьте следующий код в метод:

guard let bodyMassIndex = userHealthProfile.bodyMassIndex else {
  displayAlert(for: ProfileDataError.missingBodyMassIndex)
  return
}
ProfileDataStore.saveBodyMassIndexSample(bodyMassIndex: bodyMassIndex, date: Date())


Вы помните, что индекс массы тела является вычисленным свойством, которое возвращает значение, когда сэмплы роста и веса загружены из HealthKit. Этот код пытается вычислить данное свойство, и если возможно, он будет передан в метод savedBodyMassIndexSample (bodyMassIndex: date:), который вы только что написали.

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

Скомпилируйте и запустите приложение. Перейдите на экран Profile&BMI. Загрузите данные из HeathKit, затем нажмите кнопку Save BMI.

Посмотрите на консоль. Вы видите это?

Успешно сохраненный сэмпл BMI


Если да, поздравляю! Ваш сэмпл BMI теперь хранится в центральном хранилище HealthKit. Посмотрим, сможем ли мы его найти.

Откройте приложение «Здоровье», коснитесь вкладки «Данные о здоровье», нажмите » Измерения тела» в представлении таблицы, а затем нажмите «Индекс массы тела».

image

© Habrahabr.ru