Apple HealthKit is an SDK-based provider. Your Android consumer app would embed the Vital Mobile SDKs on a supported stack. Data are then pulled from the Apple HealthKit data store on the user’s Android device.

Refer to the Mobile SDK Installation and Vital Health SDK guides for SDK installation instructions and general API usage. This guide contains information on the the behaviour and required configuration specific to the Apple HealthKit integration.

Sync Frequency

App stateBehaviour
ForegroundApple HealthKit delivers any buffered and new changes immediately.
BackgroundHourly batch delivery of changes, subject to operating system throttling.

While the SDK schedules hourly data sync with Apple HealthKit, iOS has full discretion to defer the scheduled time based on factors like CPU usage, battery usage, connectivity, and Low Power Mode.

In other words, the Vital SDK — or any third-party HealthKit apps — cannot guarantee the sync to happen on a strict schedule. The hourly schedule is advisory and non-binding from the persective of iOS. The actual delivery frequency can therefore fluctuate from hourly, to once a day, or when certain opportunity arises (e.g., when Sleep Mode ends, or when the phone starts charging).

Background Delivery

The iOS SDK can support observing new HealthKit data even when your app is not actively being used by your app user. This provides a more seamless experience, since the app user need not periodically open your app to sync data.

1. Setup app entitlements

Start by enabling “Background Delivery” in the app capabilities:

2. Integrate with iOS system callbacks

The number one issue we have identified when integrating with Apple HealthKit, is the SDK setup sequence. Independently of the platform you are using, there are two things that need to happen in order for data to be synced:

  1. You must configure the Vital SDK at least once with VitalClient.configure, VitalClient.setUserId and VitalHealthKitClient.configure in order to get the SDK start pushing data to Vital.

    • You can configure the Vital SDK only once when a user signs into your app for the first time after app installation, or after signing out.
    • You need not call this sequence every time the app launches, as long as you follow step (2) to implement automaticConfiguration().
  2. You must call VitalHealthKitClient.automaticConfiguration() during application(_:didFinishLaunchingWithOptions:) of your UIKit UIApplicationDelegate.

    • This automatically rehydrates the Vital SDK with the last supplied configuration and Vital user ID from persistent storage. If none is found, it is a no-op.
    • The prescribed call location is critical for Background Delivery to work reliably.

The main challenge of step (2), is that for it to reliably work and be ready for deliveries from Apple HealthKit, the setup sequence in step (1) must happen. For most applications this is not possible. For example, imagine that you only enable Apple HealthKit if a user has logged in. For many apps, at didFinishLaunchingWithOptions they don’t know yet if the user is logged in or not. Or, for example, you are doing A/B testing and only a percentage of your userbase would have access to Apple HealthKit. For these reasons, VitalHealthKitClient.automaticConfiguration() is specifically designed to handle both (1) automatically recovering SDK state when the SDK has been configured once; and (2) SDK never configured or having been reset, e.g., when the user has signed out or has not ever signed in.

As per the documentation:

As soon as your app launches, HealthKit calls the update handler for any observer queries that match the newly saved data. If you plan on supporting background delivery, set up all your observer queries in your app delegate’s application(_:didFinishLaunchingWithOptions:) method. By setting up the queries in application(_:didFinishLaunchingWithOptions:), you ensure that you’ve instantiated your queries, and they’re ready to use before HealthKit delivers the updates.

In other words, make sure VitalHealthKitClient.automaticConfiguration() is called inside your UIKit App Delegate’s application(_:didFinishLaunchingWithOptions:) method.

For React Native and Flutter SDK consumers, this means you must configure this directly in native code (Swift / Objective-C). Typically, an App Delegate would have been already created as part of your generated React Native or Flutter Xcode project.

Your AppDelegate should then look like this:

Swift / Flutter iOS:

import VitalHealthKit

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

    VitalHealthKitClient.automaticConfiguration()
    /// your code

    return true
  }
}

Objective-C / React Native iOS:

#import "AppDelegate.h"
#import "VitalHealthKitConfiguration.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  [VitalHealthKitConfiguration automaticConfiguration];
  /// your code

  return YES;
}

From our experience with how the SDK is used, what tends to exist is a flow that allows the end-user to connect with Apple HealthKit. Usually a screen would ask the user if they would like to sync with Apple HealthKit. If the user accepts, you would then call:

  1. VitalClient.configure
  2. VitalClient.setUserId
  3. VitalHealthKitClient.configure

These three calls alongside VitalHealthKitClient.automaticConfiguration at didFinishLaunchingWithOptions tends to be enough to remove most issues we have seen when integrating with Apple HealthKit. We use this approach on all our examples and our Vital App.

3. Epilogue

You are all set! Once background delivery is enabled, there is no need for your app to call syncData() manually. Depending on the HKSampleType syncing, updates will come after a period of time.

As per the documentation:

HealthKit wakes your app whenever a process saves or deletes samples of the specified type. The system wakes your app at most once per time period defined by the specified frequency. Some sample types have a maximum frequency of HKUpdateFrequency.hourly. The system enforces this frequency transparently.

For example, on iOS, stepCount samples have an hourly maximum frequency.

This means that although we have background delivery’s frequency set to .hourly, we cannot guarantee hourly syncing on the dot.