I think I finally have found a way to save a workout with a route even when the iPhone is locked.
TLDR: We should create the workout ourselves and not use a HKWorkoutBuilder.
I use the following code:
let healthStore = HKHealthStore()
let energyBurned = HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: totalEnergyBurned)
let distance = HKQuantity(unit: HKUnit.meter(), doubleValue: workout.distance)
let hkworkout = HKWorkout(activityType: .running,
start: workout.start,
end: workout.end,
duration: workout.duration,
totalEnergyBurned: energyBurned,
totalDistance: distance,
metadata: nil)
try await healthStore.save(hkworkout)
let route = HKWorkoutRouteBuilder(healthStore: healthStore, device: .local())
try await route.insertRouteData(locations)
try await route.finishRoute(with: hkworkout, metadata: nil)
The tip is to directly create the workout as the HKWorkoutBuilder.finishWorkout() would return nil on a locked iPhone (why?) and then use a HKWorkoutRouteBuilder and call finishRoute() with the created workout.
Hope it will help others.