From 5bc82ec02423f5da7039e14e874018daa9b2a8e6 Mon Sep 17 00:00:00 2001 From: dietrich ayala Date: Wed, 25 Feb 2026 14:22:45 +0100 Subject: [PATCH] Fix workout activity copying and document splits limitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move workout activity addition before samples for proper association, fix min(end, end) shadowing bug, add error handling. Document that Apple Fitness only shows splits for Apple's native Workout app (since iOS 14) — no API or workaround exists. --- README.md | 1 + WorkoutEditor/HealthKitManager.swift | 40 +++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 44d7a8b..4931f5f 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ If the original workout was created by our app, it is automatically deleted afte - **Activity icons in Apple Fitness are source-dependent.** Even though we correctly set `locationType` (e.g., `.indoor`) and carry over `HKMetadataKeyIndoorWorkout`, Apple Fitness may display a generic icon for workouts from third-party sources instead of the specialized icon (e.g., indoor run icon) shown for Apple Watch workouts. - **Apple Fitness may crash when sharing copied workouts.** This is a known Apple bug where Fitness crashes when trying to view interval details or share workouts created by third-party apps via `HKWorkoutBuilder`. This is not specific to Overrun. - **No deep link to specific workouts.** Apple provides no URL scheme to open a specific workout in Fitness or Health. `activitytoday://` opens Fitness to the activity rings view only. +- **Splits are not shown for third-party workouts.** Since iOS 14, Apple Fitness only displays per-kilometer/mile splits for workouts created by Apple's native Workout app. The underlying distance samples exist in HealthKit, but Fitness will not render the splits table for workouts from any third-party source. There is no API or workaround. - **Running dynamics may not be present.** Running speed, power, stride length, vertical oscillation, and ground contact time are only recorded by Apple Watch Series 6+ / Ultra with watchOS 9+. Older devices or non-running workouts will not have these samples. ### Strava diff --git a/WorkoutEditor/HealthKitManager.swift b/WorkoutEditor/HealthKitManager.swift index 29c7d3c..092e554 100644 --- a/WorkoutEditor/HealthKitManager.swift +++ b/WorkoutEditor/HealthKitManager.swift @@ -327,6 +327,25 @@ class HealthKitManager { try await builder.beginCollection(at: start) + // Add workout activities first so builder can associate samples with them + for activity in original.workoutActivities { + let activityStart = max(activity.startDate, start) + let activityEnd = min(activity.endDate ?? end, end) + guard activityStart < activityEnd else { continue } + do { + let newActivity = HKWorkoutActivity( + workoutConfiguration: activity.workoutConfiguration, + start: activityStart, + end: activityEnd, + metadata: activity.metadata + ) + try await builder.addWorkoutActivity(newActivity) + print("[Trim] Added workout activity: \(activity.workoutConfiguration.activityType.rawValue)") + } catch { + print("[Trim] Could not copy workout activity: \(error.localizedDescription)") + } + } + // Recreate samples with new objects if !samples.isEmpty { let newSamples: [HKQuantitySample] = samples.map { s in @@ -346,27 +365,6 @@ class HealthKitManager { try await builder.addWorkoutEvents(filteredEvents) } - // Copy workout activities (needed for splits display in Apple Fitness) - for activity in original.workoutActivities { - let activityStart = max(activity.startDate, start) - let activityEnd: Date - if let end = activity.endDate { - activityEnd = min(end, end) - } else { - activityEnd = end - } - if activityStart < activityEnd { - let newActivity = HKWorkoutActivity( - workoutConfiguration: activity.workoutConfiguration, - start: activityStart, - end: activityEnd, - metadata: activity.metadata - ) - try await builder.addWorkoutActivity(newActivity) - print("[Trim] Added workout activity: \(activity.workoutConfiguration.activityType.rawValue)") - } - } - if let metadata, !metadata.isEmpty { try await builder.addMetadata(metadata) } -- 2.51.2