Adding workoutEffortScore to HKWorkout

I'm trying to hook into the new workoutEffort score supported in iOS 18, I am collecting this information from users when they submit their workout and trying to add a sample to the HKWorkout in the same manner as I've been adding other samples like bodyweight, calories burned, etc.

I'm receiving the error: HKWorkout: Sample of type HKQuantityTypeIdentifierWorkoutEffortScore must be related to a workout

I tried adding the samples using HKWorkoutBuilder.add([samples]) as which has been working perfectly for calories burned & bodyweight, but I am receiving the above error for workoutEffortScore

As a second approach, I tried adding the sample after I called finishWorkout on the HKWorkoutBuilder and received back the HKWorkout object using HKHealthStore.add([samples], to: HKWorkout) and am still receiving the same error!

I don't know otherwise how to relate a sample to a workout, I thought those were the APIs to do so? I'm using Xcode 16.0 RC (16A242) and testing on an iOS 16 Pro simulator

Answered by DTS Engineer in 803478022

The following API, which was newly introduced this year, should relate a workout effort sample to a workout:

You might give it a try and follow up here if that doesn't work.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

The following API, which was newly introduced this year, should relate a workout effort sample to a workout:

You might give it a try and follow up here if that doesn't work.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I can't get it working when using relateWorkoutEffortSample. The completion comes back without an error but there doesn't seem to be an effort stored for the workout when I view it in the Fitness app.

My code is:

    HKUnit* effortUnits = [HKUnit appleEffortScoreUnit];
    HKQuantity* effortQuantity = [HKQuantity quantityWithUnit:effortUnits doubleValue:effort];
    HKQuantityType* effortQuantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierWorkoutEffortScore];
    HKQuantitySample* effortSample = [HKQuantitySample quantitySampleWithType:effortQuantityType quantity:effortQuantity startDate:self.startDate endDate:self.endDate];
    
    for (HKWorkoutActivity* activity in self.workout.workoutActivities)
        [self.store relateWorkoutEffortSample:effortSample withWorkout:self.workout activity:activity completion:^(BOOL success, NSError * _Nullable error) {
            if (!success)
            {
                if (error)
                    NSLog(@"Error saving effort score: %@", error.localizedDescription);
                else
                    NSLog(@"Error saving effort score");
            }
            else
                NSLog(@"Successfully saved effort score %d", effort);
        }];

Your code looks good to me, except that it doesn't provide the whole picture. For example:

  • It doesn't show the value of effort. The range of a workout score needs to be from 0 to 10.
  • It doesn't show if you have the right authentication. To relate a workout effort sample, you need to have the authentication to read and share the sample types:
let typesToShare: Set = [
    HKQuantityType.workoutType(),
    HKQuantityType(.workoutEffortScore)
]
let typesToRead: Set = [
    HKQuantityType.workoutType(),
    HKQuantityType(.workoutEffortScore)
]
...
try await healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead)

The following code works for me, where workout is an existing workout retrieved from my HealthKit store:

if #available(iOS 18, *) {
    let effortUnit = HKUnit.appleEffortScore()
    let effortQuantity = HKQuantity(unit: effortUnit, doubleValue: 8.0)
    let effortQuantityType = HKQuantityType.quantityType(forIdentifier: .workoutEffortScore)!
    let effortSample = HKQuantitySample(type: effortQuantityType,
                                        quantity: effortQuantity,
                                        start: workout.startDate,
                                        end: workout.endDate)
    Task {
        do {
            try await WorkoutManager.shared.healthStore.relateWorkoutEffortSample(effortSample, with: workout, activity: nil)
        } catch let error {
            print("\(error)")
        }
    }
}

I tweaked the Building a multidevice workout app sample to run the above code in ChartView on my iPhone. After that, I did see that my Fitness.app shows "8 Hard" in the "Effort" section of the "workout Details" view.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Many thanks for your detailed response. I finally managed to get it working by coding it in Swift, using your example code. For some reason I just couldn't get it working with Objective C.

Adding workoutEffortScore to HKWorkout
 
 
Q