HealthKit

The Validic Mobile library provides a simple way to read and upload data from HealthKit to Validic. VLDHealthKitManager can subscribe to specific HealthKit sample types and automatically upload them to Validic in the background as new data is recorded.

Before you can use the HealthKit features of the library, be sure to import the HealthKit framework into your project and enable the HealthKit entitlements for your app.

Note: Starting in Xcode 13 & iOS 15, your app needs a new entitlement added to the entitlements file. This new entitlement is called “HealthKit Observer Query Background Delivery” and should be set to “YES”:

HealthKit Background Entitlement

If viewing the raw entitlements file, add the new key and value as displayed below:

<key>com.apple.developer.healthkit.background-delivery</key>
<true/>

Setup

  • The HealthKit framework requires filling in the Privacy - Health Share Usage Description and Privacy - Health Update Usage Description keys and values if edited in Xcode.

HealthKit Usage Description

If viewing the raw source add the NSHealthShareUsageDescription and NSHealthUpdateUsageDescription keys as displayed below.

<key>NSHealthShareUsageDescription</key>
<string>Get Health data</string>
<key>NSHealthUpdateUsageDescription</key>
<string>Write Health data</string>

To use HealthKit in your project you must enable it from the “Signing & Capabilities” tab. Select your project target and then select the “Signing & Capabilities” tab. Click “+ Capability”, scroll down until you find HealthKit, and double-click it to add HealthKit to the project. Make sure to check the box next to “Background Delivery” in the newly-added HealthKit section. Your project should look like the screenshot below.

HealthKit-Enabled

In new Xcode projects the HealthKit framework will be linked automatically when the framework is imported by a project file. To confirm this navigate to the Build Settings tab of the project target and search for Link Frameworks. Under Under Clang - Language - Modules you’ll see Link Frameworks Automatically set to Yes.

Linked Frameworks

Subscription sets

Useful sets of sample types are defined in an VLDHealthKitSubscriptionSet enum. Sample types for a subscription set can be retrieved using the sampleTypesForSubscriptionSet: static method of the VLDHealthKitSubscription class. The following subscription sets are available:

  • VLDHealthKitSubscriptionSetRoutine: Includes Flights Climbed, Active Energy Burned, Distance Walking/Running, Step Count, Basal Energy Burned, Apple Stand Hours, Apple Exercise Minutes, Mindful Minutes (from the Apple Watch), Wheelchair Distance, and Wheelchair Push Count.
  • VLDHealthKitSubscriptionSetDiabetes: Blood Glucose sample type.
  • VLDHealthKitSubscriptionSetWeight: Includes Body Mass (weight), Height, Body Fat Percentage, Lean Body Mass, and Body Mass Index.
  • VLDHealthKitSubscriptionSetFitness: Includes Workout, Nike Fuel, and Cycling Distance.
  • VLDHealthKitSubscriptionSetSleep: Sleep Analysis sample type, this set tracks time in bed, time awake, and time asleep.
  • VLDHealthKitSubscriptionSetBasicNutrition: Includes Calcium, Carbohydrates, Cholesterol, Fiber, Iron, Potassium, Protein, Saturated Fat, Sodium, Sugar, Total Fat, Energy Consumed, and Dietary Water.
  • VLDHealthKitSubscriptionSetReproductiveHealth: Includes Sexual Activity, Cervical Mucus Quality, Intermenstrual Bleeding, Menstrual Flow, Ovulation Test Result, and BasalBodyTemperature.
  • VLDHealthKitSubscriptionSetBiometrics: Includes Systolic Blood Pressure, Diastolic Blood Pressure, Heart Rate, Body Temperature, and SpO2 (oxygen saturation).

Subscribe to changes

Subscribing to HealthKit updates only needs to be done once for a user. The subscriptions will be persisted across app launches in the VLDSession object. A typical use of the HealthKit framework would be to create a UISwitch that adds the required subscriptions when turned on and removes them when turned off. Example:

func toggleHealthKit(_ sender: UISwitch) {
    if sender.isOn {
        var sampleTypes = VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.routine)
        sampleTypes?.append(contentsOf: VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.biometrics))
        sampleTypes?.append(contentsOf: VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.diabetes))
        sampleTypes?.append(contentsOf: VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.sleep))
        sampleTypes?.append(contentsOf: VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.basicNutrition))
        sampleTypes?.append(contentsOf: VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.fitness))
        sampleTypes?.append(contentsOf: VLDHealthKitSubscription.sampleTypes(for: VLDHealthKitSubscriptionSet.weight))

        VLDHealthKitManager.sharedInstance().setSubscriptions(sampleTypes, completion: nil)
    } else {
        VLDHealthKitManager.sharedInstance().setSubscriptions([], completion: nil)
    }
}

Retrieve data continuously

To properly enable the continuous delivery of data in the background or foreground, the subscription observers need to be recreated immediately when the app is launched from a suspend or terminated state. To do this, you need to call [VLDHealthKitManager observeCurrentSubscriptions] inside your application delegate’s application:didFinishLaunchingWithOptions: function. Example:

func application(_ application: UIApplication didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
    VLDHealthKitManager.sharedInstance().observeCurrentSubscriptions()
    return true
}

Note: Calling [[VLDSession] sharedInstance] endSession] in Objective-C or VLDSession.sharedInstance().end() in Swift will remove all HealthKit subscriptions and stop listening for new data.

Subscribe to Intraday steps

The Validic Mobile library supports retrieving more fine grain step data through VLDIntraday. If enabled via your SDK this will automatically be gathered when subscripting to VLDHealthKitSubscriptionSetRoutine. This is a premium feature and is not enabled by default, if you have access you can set processIntraday to YES to get intraday step data sent to Inform.

Fetch historical data

The Validic Mobile library provides the ability to query up to 180 days of historical data for a subset of data types provided by HealthKit by specifying one of more values of the VLDHealthKitHistoricalSet enum.

Two historical sets are available:

  • VLDHealthKitHistoricalSetRoutine - Step data
  • VLDHealthKitHistoricalSetFitness - Workout data
  • VLDHealthKitHistoricalSetIntraday - Intraday step data (Premium feature, with only 7 days of historical data)

To fetch historical HealthKit data, call the fetchHistoricalSets: method on VLDHealthKitManager and pass in the datasets you want to fetch, along with from and to parameters.

The from and to parameters should be DateComponents with the year, month, and day components defined. Data is always pulled in full-day increments, so any time data on the DateComponents (hour, minute, second, etc) will be ignored. Historical data cannot go back more than 179 days before the current day.

Example pulling 30 days of history:

let toDate = Calendar.current.startOfDay(for: Date())
let toDateComponents = Calendar.current.dateComponents([.year, .month, .day], from: toDate)
let fromDate = Calendar.current.date(byAdding: .day, value: -30, to: toDate, wrappingComponents: false)!
let fromDateComponents = Calendar.current.dateComponents([.year, .month, .day], from: fromDate)

VLDHealthKitManager.sharedInstance().fetchHistoricalSets([NSNumber(value:VLDHealthKitHistoricalSet.fitness.rawValue),
                                                          NSNumber(value:VLDHealthKitHistoricalSet.routine.rawValue)]),
                                                          from: fromDateComponents,
                                                          to: toDateComponents
                                                          { (results:[AnyHashable : Any]?, error:Error?) in
    // historical fetch complete
}

Doing this may display a permission dialog from HealthKit, so it’s important to call this at an appropriate time in your app. It is recommended to explain to the user why you want this data before attempting to fetch it. This operation may take several seconds to complete so it would be advisable to display an activity indicator to the user until the completion block is called. When the fetch completes all the data will have been fetched locally and queued up for submission to the server. The amount of time needed to upload the records may vary based on the amount of history fetched and the user’s internet speed. The queued records are uploaded automatically by the library based on connectivity, but it is possible for the user to suspend the app before all the records have been uploaded, the remaining records will be uploaded when the user resumes the app. This should be kept in mind when receiving records from the Validic server.

Considerations

Passive Reading Sync

There are situations where Apple Health passive reading is stopped and requires the app to be relaunched. These situations include:

  • When the user explicitly kills the host app by swiping up on it from the mutitasking view.
  • When the OS kills the app due to low memory, low battery, etc.
  • When the device is rebooted.

The user must restart the host app to resume passive reading of Apple Health data.