# Overrun
An iOS app for trimming and managing HealthKit workouts. Built for the common problem of forgetting to stop recording — leaving you with an 8-hour "run" that wrecks your stats.
## Features
### Workout List
- Animated gradient header with icon colors (cyan, lime green, hot pink)
- Workouts grouped by month/year with section headers
- **Search** by activity type name (case-insensitive)
- **Sort** by date (default), longest duration, or shortest duration — useful for quickly finding forgot-to-stop workouts or accidental recordings
- Paginated loading (50 at a time) with infinite scroll
- Pull-to-refresh and automatic refresh via HealthKit observer (updates when workouts are added or deleted externally)
### Guided Trim Flow
The workout detail view walks you through a step-by-step process:
1. **Trim and save** — Activity graph showing heart rate, active energy, or distance over time. Range slider to select the portion to keep, with live-updating start/end times, duration, and activity type picker. Explainer text describes what data is preserved and what is lost.
2. **Upload to Strava** (optional) — Upload the trimmed workout directly to Strava via OAuth. Shown after saving, with connect/upload/status inline.
3. **Remove the original** — Instructions to delete the original in Apple Fitness (swipe left → "Delete Workout & Data"), with an "Open Fitness" button. If the original was created by Overrun, it is automatically deleted.
Steps 2 and 3 are visible but dimmed before step 1 is complete, so you know the full process before you begin.
### Data-Preserving Trim
When you trim a workout, Overrun creates a new workout and migrates all associated data within the trimmed time range:
- **Heart rate, active/basal energy, distance, step count** samples
- **Running dynamics** — speed, power, stride length, vertical oscillation, ground contact time
- **VO2 max** and **physical effort** samples
- **Workout effort scores** (iOS 18+, user-entered only)
- **Workout routes** (GPS/location data)
- **Workout events** (laps, segments, etc.)
- **Metadata** (indoor/outdoor flag, weather, timezone, METs, etc.)
- **Configuration** — activity type, indoor/outdoor location type, swimming config
### Strava Integration
- OAuth authentication via the Strava app (falls back to Safari if not installed)
- Direct upload of workouts as TCX files with heart rate and GPS data
- Upload status tracking with polling (uploading → processing → success)
- Connect/disconnect from anywhere in the trim flow
## Known Limitations
### HealthKit / Apple Platform
- **Cannot modify workouts in place.** HealthKit provides no API to change a workout's start/end dates, samples, or metadata. Trimming must create a new workout and delete the original.
- **Cannot delete workouts from other apps.** HealthKit only allows an app to delete objects it created. Workouts from Apple Watch, Fitness+, or other apps must be deleted manually by the user (e.g., swipe left in Apple Fitness and choose "Delete Workout & Data").
- **Estimated workout effort is not copyable.** `estimatedWorkoutEffortScore` is computed by Apple's algorithms and cannot be written by third-party apps. It may or may not be recomputed by the system for the new workout. User-entered effort scores (`workoutEffortScore`, iOS 18+) are migrated via `relateWorkoutEffortSample`.
- **Physical effort samples are system-generated.** `physicalEffort` samples are computed by the OS and cannot be created or associated by third-party apps.
- **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
- **Strava only imports workouts from Apple's native Workout app.** Strava checks the `sourceRevision` bundle identifier on each HealthKit workout and only accepts workouts from Apple's Workout app and Apple Fitness+. Workouts written by any third-party app (including Overrun) are intentionally excluded from Strava's HealthKit import list, regardless of how complete the data is.
- **This is a Strava policy, not a data issue.** The workout data we write is fully valid and appears correctly in Apple Health and Apple Fitness.
- **Direct upload works.** Overrun bypasses the HealthKit import limitation by uploading directly to Strava's API via OAuth + TCX file upload.
## Requirements
- iOS 17+
- Xcode 15+
- HealthKit entitlement
## Building
Open `WorkoutEditor.xcodeproj` in Xcode and run on a device or simulator. The app requires HealthKit authorization to read and write workouts and associated samples.
### Strava Setup
1. Register an API application at [strava.com/settings/api](https://www.strava.com/settings/api)
2. Set the Authorization Callback Domain to `localhost`
3. Copy `Secrets.xcconfig.template` to `Secrets.xcconfig` and fill in your Client ID and Client Secret
4. `Secrets.xcconfig` is gitignored and will not be committed
In debug builds, 200 sample workouts are auto-generated on first launch for testing.
## Localization
The app is set up for internationalization using Xcode String Catalogs (`.xcstrings`). All user-facing strings in SwiftUI views are automatically extracted by Xcode.
### Contributing a Translation
1. Open the project in Xcode
2. Go to **Project Settings → Info → Localizations** and add your language
3. Xcode will create localized entries in `Localizable.xcstrings` and `InfoPlist.xcstrings`
4. Open `Localizable.xcstrings` in Xcode's String Catalog editor and translate each string
5. `InfoPlist.xcstrings` contains the app name and HealthKit usage descriptions — translate those too
6. Activity type names (Running, Cycling, etc.) and all step/button labels are included in the catalog
### Files
- `WorkoutEditor/Localizable.xcstrings` — all app UI strings
- `WorkoutEditor/InfoPlist.xcstrings` — Info.plist strings (app name, permission descriptions)