ReviewOS

also looking at this

craft-native/craft

iOS: saveHealthWorkout closes the health tier — ratchet 29 → 28

#100
Merged glennmichael123 wants to merge feat/ios-health-workout into main
3 files +716 -44
10 days ago

The third and last HealthKit action, deferred one round for its cost, not for correctness.

Unlike openPDF/closePDF and the audio pair — which shared module state and had to move whole — these three share only a stateless HKHealthStore, so a split was never at risk of dividing state. What it cost was a third asynchronous stage.

Three stages, none able to carry what the next needs

save:withCompletion: → insertRouteData:completion: → finishRouteWithWorkout:metadata:completion:. Each can fail after the previous succeeded, and all three blocks are global so they capture nothing. WorkoutChain rides the slot between them: each stage takes the entry and a continuing stage puts it back, so a duplicate fire finds nothing rather than releasing the workout twice.

Two divergences, both the same direction

Swift rejects when a route fails to build, insert or finish — after the workout has already been written to HealthKit. That tells a page nothing was saved when something was, and leaves it unable to reference a workout that exists. Here a route failure still resolves the workout's id, with the reason logged.

Reading the enum instead of guessing it

Every HKWorkoutActivityType value was read out of HKWorkout.h, and every one reads wrong on instinct:

activityinstinctactual
running~37 (near walking)34
walking~5249
hiking~2422
cycling1313

The enum numbers most members implicitly, so the values are only obtainable by counting from the last explicit one. A wrong number saves a real workout under the wrong activity, and HealthKit accepts it without complaint — no crash, no diagnostic, just a run recorded as something else.

That is last round's HKStatisticsOptionCumulativeSum lesson applied before the mistake instead of after it. The same discipline caught the route-builder selector, which is finishRouteWithWorkout:metadata:completion: and not the finishRoute(with:) the Swift call site suggests.

Other behaviour carried across

  • A non-positive distance or energy is dropped, not saved as zero (Swift's flatMap). An explicit zero reads as "measured none"; nil reads as "did not measure".
  • Locations that will not parse are skipped rather than refused (Swift's compactMap), so a workout with ten malformed fixes still saves — with no route, which is the same early exit an empty array takes.
  • Equal start and end dates are refused, because Swift's guard is endValue > startValue, not >=.

Verification

zig build test green (153/153), zig fmt --check, build-ios and build-ios-simulator clean, pickier . 0 errors.

37 fixture assertions pass on a simulator — unchanged. saveHealthWorkout needs write authorization to reach any interesting path, and simctl has no HealthKit privacy service; that is the same wall requestHealthAuthorization hits. The host tests cover the guard, the activity values, the millisecond conversion, the drop-vs-zero rule, and that each of the three stages ignores a fire for a slot holding no chain.

3 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.