Class

HKWorkout

The HKWorkout class is a concrete subclass of the HKSample class. The workout object records information about a single physical activity in the HealthKit store. HealthKit supports a wide range of activity types. The workout object not only stores summary information about the activity (for example, duration, total distance, and total energy burned), it also acts as a container for other HKSample objects. You can associate any number of samples with a workout, letting you track changes to the workout over time. For example, you may want to break a single run into a number of intervals, and then add samples to track the user’s heart rate, energy burned, distance traveled, and steps taken for each interval.

Overview

Workouts are mostly immutable. You set their properties when you instantiate the workout, and they cannot change. However, you can continue to add samples to the workouts.

Adding Samples to a Workout

You can associate samples with a workout using the HealthKit store’s add(_:to:completion:) method. The workout must be saved to the HealthKit store before you can add any samples. The samples don’t need to be saved. Adding them to the workout automatically saves them (if they have not been saved already). The following sample shows how to associate energy burned and heart rate samples with a workout.

// This sample uses hard-coded values and performs all the operations inline
// for simplicity's sake. A real-world app would calculate these values
// from sensor data and break the operation up using helper methods.
 
let energyBurned = HKQuantity(unit: HKUnit.kilocalorieUnit(),
                              doubleValue: 425.0)
 
let distance = HKQuantity(unit: HKUnit.mileUnit(),
                          doubleValue: 3.2)
 
// Provide summary information when creating the workout.
let run = HKWorkout(activityType: HKWorkoutActivityType.Running,
                    startDate: start, endDate: end, duration: 0,
                    totalEnergyBurned: energyBurned, totalDistance: distance, metadata: nil)
 
// Save the workout before adding detailed samples.
healthStore.saveObject(run) { (success, error) -> Void in
    guard success else {
        // Perform proper error handling here...
        fatalError("*** An error occurred while saving the " +
            "workout: \(error?.localizedDescription)")
    }
    
    // Add optional, detailed information for each time interval
    var samples: [HKQuantitySample] = []
    
    guard let distanceType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning) else {
        fatalError("*** Unable to create a distance type ***")
    }
    
    let distancePerInterval = HKQuantity(unit: HKUnit.footUnit(),
                                         doubleValue: 165.0)
    
    let distancePerIntervalSample =
        HKQuantitySample(type: distanceType, quantity: distancePerInterval,
                         startDate: intervals[0], endDate: intervals[1])
    
    samples.append(distancePerIntervalSample)
    
    guard let energyBurnedType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned) else {
        fatalError("*** Unable to create an energy burned type ***")
    }
    
    let energyBurnedPerInterval = HKQuantity(unit: HKUnit.kilocalorieUnit(),
                                             doubleValue: 15.5)
    
    let energyBurnedPerIntervalSample =
        HKQuantitySample(type: energyBurnedType, quantity: energyBurnedPerInterval,
                         startDate: intervals[0], endDate: intervals[1])
    
    samples.append(energyBurnedPerIntervalSample)
    
    guard let heartRateType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate) else {
        fatalError("*** Unable to create a heart rate type ***")
    }
    
    let heartRateForInterval = HKQuantity(unit: HKUnit(fromString: "count/min"),
                                          doubleValue: 95.0)
    
    let heartRateForIntervalSample =
        HKQuantitySample(type: heartRateType, quantity: heartRateForInterval,
                         startDate: intervals[0], endDate: intervals[1])
    
    samples.append(heartRateForIntervalSample)
    
    // Continue adding detailed samples...
    
    // Add all the samples to the workout.
    self.healthStore.addSamples(samples,
                                toWorkout: run) { (success, error) -> Void in
                                    guard success else {
                                        // Perform proper error handling here...
                                        fatalError("*** An error occurred while adding a " +
                                            "sample to the workout: \(error?.localizedDescription)")
                                    }
    }
}

You can create a query that returns only the samples associated with a workout by adding the workout to the query’s predicate. The predicateForObjects(from:) method creates a predicate object that matches only samples associated with the provided workout.

guard let distanceType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning) else {
    fatalError("*** Unable to create the distance type ***")
}
 
let workoutPredicate =
    HKQuery.predicateForObjectsFromWorkout(workout)
 
let startDateSort =
    NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
 
let query = HKSampleQuery(sampleType: distanceType, predicate: workoutPredicate,
                          limit: 0, sortDescriptors: [startDateSort]) {
                            (sampleQuery, results, error) -> Void in
                            
                            guard let distanceSamples = results as? [HKQuantitySample] else {
                                // Perform proper error handling here...
                                fatalError("*** An error occurred while adding a sample to " +
                                    "the workout: \(error?.localizedDescription)")
                            }
                            
                            // process the detailed samples...
                            
}
 
healthStore.executeQuery(query)

Associating samples with a workout provides fine-grain information about the workout. However, adding samples to the workout does not change any of the workout’s properties. Specifically, adding distance samples won’t change the quantity stored in the totalDistance, startDate, endDate or duration properties. Likewise, adding active energy burned samples won’t change the quantity stored in the totalEnergyBurned property. This can lead to some duplication in data between the workout’s properties and the associated samples.

Your app should always provide data for the workout’s duration, totalDistance, and totalEnergyBurned properties when the data is both available and relevant to the workout. In addition, you should provide a set of associated samples that sum up to these totals. You can also provide additional associated samples, to help track how the intensity of the exercise changed during the course of the workout.

For example, imagine an app that tracks a user’s runs. Whenever a user finishes a run, the app creates a workout that includes the total distance, duration, and calories burned for the entire run. The app also saves samples that describe the distance, calories burned, step count, heart rate, flights climbed, and other data over much smaller time intervals.

You need to fine tune the exact length of your associated samples based on the type of workout and the needs of your app. Using five-minute intervals minimizes the amount of memory needed to store the workouts, while still providing a general sense of the change in intensity over the course of a long workout. Using five-second intervals provides a much-more detailed view of the workout, but requires considerably more memory and processing.

Subclassing Notes

Like many HealthKit classes, the HKWorkout class should not be subclassed.

You may extend workouts by adding metadata with custom keys as appropriate for your app. For more information, see the methods init(activityType:start:end:duration:totalEnergyBurned:totalDistance:metadata:) and init(activityType:start:end:workoutEvents:totalEnergyBurned:totalDistance:metadata:).

Topics

Creating Workouts

init(activityType: HKWorkoutActivityType, start: Date, end: Date)

Instantiates and returns a new workout activity.

init(activityType: HKWorkoutActivityType, start: Date, end: Date, duration: TimeInterval, totalEnergyBurned: HKQuantity?, totalDistance: HKQuantity?, metadata: [String : Any]?)

Instantiates and returns a new workout activity that includes the total energy burned, total distance, and metadata for the workout.

init(activityType: HKWorkoutActivityType, start: Date, end: Date, workoutEvents: [HKWorkoutEvent]?, totalEnergyBurned: HKQuantity?, totalDistance: HKQuantity?, metadata: [String : Any]?)

Instantiates and returns a workout whose duration is calculated based on the start and end dates and the provided workout events. This method also specifies the total distance, total energy, and metadata properties.

init(activityType: HKWorkoutActivityType, start: Date, end: Date, duration: TimeInterval, totalEnergyBurned: HKQuantity?, totalDistance: HKQuantity?, device: HKDevice?, metadata: [String : Any]?)

Instantiates and returns a new workout activity that includes the total energy burned, total distance, device, and metadata for the workout.

init(activityType: HKWorkoutActivityType, start: Date, end: Date, workoutEvents: [HKWorkoutEvent]?, totalEnergyBurned: HKQuantity?, totalDistance: HKQuantity?, device: HKDevice?, metadata: [String : Any]?)

Instantiates and returns a workout whose duration is calculated based on the start and end dates and the provided workout events. This method also specifies the total distance, total energy, device, and metadata properties.

Getting Property Data

var duration: TimeInterval

The workout’s duration.

var totalDistance: HKQuantity?

The total distance traveled during the workout.

var totalEnergyBurned: HKQuantity?

The total active energy burned during the workout.

var workoutActivityType: HKWorkoutActivityType

The type of activity performed during the workout.

var workoutEvents: [HKWorkoutEvent]?

An array of workout event objects.

Constants

Sort Identifiers

Constants that can be used to sort workouts.

Predicate Key Paths

Constants that can be used to create predicate format strings.

Relationships

Inherits From

Beta Software

This documentation contains preliminary information about an API or technology in development. This information is subject to change, and software implemented according to this documentation should be tested with final operating system software.

Learn more about using Apple's beta software