Created
February 21, 2021 17:34
-
-
Save shaundon/655011690f8d257c67e6eb6fc81e59d8 to your computer and use it in GitHub Desktop.
Convert an array of HKQuantitySample into splits.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import HealthKit | |
struct WorkoutSplit: Hashable { | |
let label: String | |
let distance: HKQuantity | |
let duration: TimeInterval | |
} | |
extension WorkoutSplit { | |
static func array( | |
fromHKQuantitySamples samples: [HKQuantitySample], | |
fromStartDate workoutStartDate: Date, | |
withDistanceUnit distanceUnit: HKUnit | |
) -> [WorkoutSplit] { | |
var splits = [WorkoutSplit]() | |
// The size of each split, e.g. 1 km / 1 mile | |
let splitSize: Double = 1.0 | |
// A place to keep a reference to the previous iteration of the loop, so that | |
// any time gaps between measurements can be accounted for. | |
var previousSample: HKQuantitySample? = nil | |
var currentDistanceTotal: Double = 0 | |
var currentDurationTotal: TimeInterval = 0 | |
var splitCount = 1 | |
for sample in samples { | |
let distance = sample.quantity.doubleValue(for: distanceUnit) | |
let duration = sample.endDate - sample.startDate | |
// Add the current sample to the running total. | |
currentDistanceTotal += distance | |
currentDurationTotal += duration | |
// Check for gaps between sample times, and add them where necessary. | |
if let previousSample = previousSample { | |
let durationGapBetweenSamples = sample.startDate - previousSample.endDate | |
if durationGapBetweenSamples > 0 { | |
currentDurationTotal += durationGapBetweenSamples | |
} | |
} | |
// Otherwise this is the first sample, so take into account the gap between the workout's start | |
// date and the start of this sample | |
else { | |
let gapBetweenWorkoutStartingAndFirstSample = sample.startDate - workoutStartDate | |
currentDurationTotal += gapBetweenWorkoutStartingAndFirstSample | |
} | |
// If the current split is over 1km/1mi, we need to reset and start a new split. | |
if currentDistanceTotal > splitSize { | |
// How much the current distance 'overflows' the split by. | |
let distanceOverflow = currentDistanceTotal - splitSize | |
// We need to adjust the durations for this and next split to account for the overflow. | |
let durationProportionBelongingToThisSplit = (distance - distanceOverflow) / distance | |
let durationProportionBelongingToNextSplit = 1 - durationProportionBelongingToThisSplit | |
let durationOverflow = duration * durationProportionBelongingToNextSplit | |
currentDurationTotal -= durationOverflow | |
// Record the split. | |
splits.append(WorkoutSplit( | |
label: "\(distanceUnit.unitString) \(splitCount)", | |
distance: HKQuantity(unit: distanceUnit, doubleValue: splitSize), | |
duration: currentDurationTotal | |
)) | |
// Reset the counters, beginning them with the overflows. | |
currentDistanceTotal = distanceOverflow | |
currentDurationTotal = durationOverflow | |
splitCount += 1 | |
} // if | |
previousSample = sample | |
} // for | |
return splits | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment