Skip to content

Instantly share code, notes, and snippets.

@shaundon
Created February 21, 2021 17:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save shaundon/655011690f8d257c67e6eb6fc81e59d8 to your computer and use it in GitHub Desktop.
Save shaundon/655011690f8d257c67e6eb6fc81e59d8 to your computer and use it in GitHub Desktop.
Convert an array of HKQuantitySample into splits.
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