Class

HKWorkout

A workout sample stores information about a single physical activity.

Declaration

@interface HKWorkout : HKSample

Overview

The HKWorkout class is a concrete subclass of the HKSample class. The workout records summary about a single physical activity (for example, the 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 add fine-grain tracking over the course of the workout. For example, you may want to break a single run into a number of shorter intervals, and then add samples to track the user’s heart rate, energy burned, distance traveled, and steps taken for each interval.

HealthKit supports a wide range of activity types. For a complete list, see HKWorkoutActivityType.

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 addSamples:toWorkout: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.
 
HKQuantity *energyBurned =
[HKQuantity quantityWithUnit:[HKUnit kilocalorieUnit]
                 doubleValue:425.0];
 
HKQuantity *distance =
[HKQuantity quantityWithUnit:[HKUnit mileUnit]
                 doubleValue:3.2];
 
// Provide summary information when creating the workout.
HKWorkout *run = [HKWorkout workoutWithActivityType:HKWorkoutActivityTypeRunning
                                          startDate:start
                                            endDate:end
                                           duration:0
                                  totalEnergyBurned:energyBurned
                                      totalDistance:distance
                                           metadata:nil];
 
// Save the workout before adding detailed samples.
[self.healthStore saveObject:run withCompletion:^(BOOL success, NSError *error) {
    if (!success) {
        // Perform proper error handling here...
        NSLog(@"*** An error occurred while saving the "
              @"workout: %@ ***", error.localizedDescription);
 
        abort();
    }
 
    // Add optional, detailed information for each time interval
    NSMutableArray *samples = [NSMutableArray array];
 
    HKQuantityType *distanceType =
    [HKObjectType quantityTypeForIdentifier:
     HKQuantityTypeIdentifierDistanceWalkingRunning];
 
    HKQuantity *distancePerInterval =
    [HKQuantity quantityWithUnit:[HKUnit mileUnit]
                     doubleValue:3.2];
 
    HKQuantitySample *distancePerIntervalSample =
    [HKQuantitySample quantitySampleWithType:distanceType
                                    quantity:distancePerInterval
                                   startDate:intervals[0]
                                     endDate:intervals[1]];
 
    [samples addObject:distancePerIntervalSample];
 
    HKQuantityType *energyBurnedType =
    [HKObjectType quantityTypeForIdentifier:
     HKQuantityTypeIdentifierActiveEnergyBurned];
 
    HKQuantity *energyBurnedPerInterval =
    [HKQuantity quantityWithUnit:[HKUnit kilocalorieUnit]
                     doubleValue:15.5];
 
    HKQuantitySample *energyBurnedPerIntervalSample =
    [HKQuantitySample quantitySampleWithType:energyBurnedType
                                    quantity:energyBurnedPerInterval
                                   startDate:intervals[0]
                                     endDate:intervals[1]];
 
    [samples addObject:energyBurnedPerIntervalSample];
 
    HKQuantityType *heartRateType =
    [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
 
    HKQuantity *heartRateForInterval =
    [HKQuantity quantityWithUnit:[HKUnit unitFromString:@"count/min"]
                     doubleValue:95.0];
 
    HKQuantitySample *heartRateForIntervalSample =
    [HKQuantitySample quantitySampleWithType:heartRateType
                                    quantity:heartRateForInterval
                                   startDate:intervals[0]
                                     endDate:intervals[1]];
 
    [samples addObject:heartRateForIntervalSample];
 
    // Continue adding additional samples here...
 
    // Add all the samples to the workout.
    [self.healthStore
     addSamples:samples
     toWorkout:run
     completion:^(BOOL success, NSError *error) {
         if (!success) {
             // Perform proper error handling here...
             NSLog(@"*** An error occurred while adding a "
                   @"sample to the workout: %@ ***",
                   error.localizedDescription);
 
             abort();
         }
     }];
 
}];

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

HKQuantityType *distanceType =
[HKObjectType quantityTypeForIdentifier:
    HKQuantityTypeIdentifierDistanceWalkingRunning];
 
NSPredicate *workoutPredicate =
[HKQuery predicateForObjectsFromWorkout:self.workout];
 
NSSortDescriptor *startDateSort =
[NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate
                              ascending:YES];
 
HKSampleQuery *query =
[[HKSampleQuery alloc]
 initWithSampleType:distanceType
 predicate:workoutPredicate
 limit:0
 sortDescriptors:@[startDateSort]
 resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
     if (results == nil) {
         // Perform proper error handling here...
         NSLog(@"*** An error occurred while adding a sample to "
               @"the workout: %@ ***",
               error.localizedDescription);
 
         abort();
     }
 
     // process the detailed samples...
 
 }];
 
[self.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.

Using Workouts

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 workoutWithActivityType:startDate:endDate:duration:totalEnergyBurned:totalDistance:metadata: and workoutWithActivityType:startDate:endDate:workoutEvents:totalEnergyBurned:totalDistance:metadata:.

Topics

Creating Workouts

+ workoutWithActivityType:startDate:endDate:

Instantiates and returns a new workout activity.

+ workoutWithActivityType:startDate:endDate:duration:totalEnergyBurned:totalDistance:metadata:

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

+ workoutWithActivityType:startDate:endDate:workoutEvents:totalEnergyBurned:totalDistance:metadata:

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.

+ workoutWithActivityType:startDate:endDate:duration:totalEnergyBurned:totalDistance:device:metadata:

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

+ workoutWithActivityType:startDate:endDate:workoutEvents:totalEnergyBurned:totalDistance:device:metadata:

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.

+ workoutWithActivityType:startDate:endDate:workoutEvents:totalEnergyBurned:totalDistance:totalFlightsClimbed:device:metadata:

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, total flights, device, and metadata properties.

+ workoutWithActivityType:startDate:endDate:workoutEvents:totalEnergyBurned:totalDistance:totalSwimmingStrokeCount:device:metadata:

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, total stroke count, device, and metadata properties.

Getting Property Data

duration

The workout’s duration.

totalDistance

The total distance traveled during the workout.

totalEnergyBurned

The total active energy burned during the workout.

workoutActivityType

The type of activity performed during the workout.

workoutEvents

An array of workout event objects.

totalFlightsClimbed

The total number of flights of stairs climbed during the workout.

totalSwimmingStrokeCount

The total stroke count for the workout.

Constants

Workout Sort Identifiers

Constants that can be used to sort workouts.

Workout Predicate Key Paths

Constants that can be used to create predicate format strings for workouts.

Workout Metadata Keys

Constants that can be used to add metadata to workouts.

Relationships

Inherits From

See Also

Samples

Using Workout Samples

Record a physical activity using workout samples.

HKWorkoutBuilder

A builder object that incrementally constructs a workout.

HKWorkoutType

A type that identifies samples that store information about a workout.

HKWorkoutTypeIdentifier

The workout type identifier.

HKWorkoutActivityType

The type of activity performed during a workout.

HKWorkoutEvent

An object representing an important event during a workout.