Health Sync and Export
Train Libre can read selected records from Apple Health on iOS or Health Connect on Android, and can write selected Train Libre records back to the same platform. The app calls the platform APIs through native method channels. This page describes the records and behavior implemented in Train Libre; it does not promise that Apple or Google will not sync their own health-store data through account or device services.
Fitness, nutrition, recovery, sleep, and pulse features are non-clinical heuristics for healthy individuals tracking performance, not medical advice. They do not apply to medical conditions, including eating or sleep disorders.
Imported data#
Health imports are opt-in by feature in Health settings. The app requests the corresponding platform permissions and stores imported records or derived aggregates in its local database. Platform authorization can be partial or unavailable, and HealthKit does not reveal whether read access was denied; a query can return an error or no visible data even after its authorization request succeeds.
| Data | Platform input and local handling | Where Train Libre uses it |
|---|---|---|
| Steps | HealthKit step-count samples on iOS; Health Connect StepsRecord on Android. The app stores positive segments with start/end times, source ID, and a native ID when supplied. A normal refresh overlaps the last sync by 48 hours; first sync looks back 30 days. | The Diary and Statistics views aggregate local segments. The current default is auto_dominant: choose the source with the most steps for that day, then total that source. The legacy max_per_hour option exists in aggregation code, but the settings service currently always returns auto_dominant. |
| Sleep | HealthKit sleep-analysis samples on iOS; Health Connect sleep sessions/stages on Android. The importer maps platform records into raw imports and canonical sessions/stage segments. Initial import looks back 90 days; later imports revisit 72 hours before the latest imported session end to catch changes. | The Sleep Health Score pipeline reads canonical sleep sessions/stages. Missing stage detail remains missing; an imported session does not imply all stages were measured. See Sleep Health Score. |
| Heart rate | The app queries platform heart-rate samples when pulse analysis needs them. Valid finite positive BPM samples are grouped into local hourly aggregates; the app stores aggregate counts, min/max/sum, and first/last sample times rather than retaining a permanent raw-sample table for this feature. | Pulse summaries and charts use the local aggregates. Sleep imports may also carry heart-rate samples for the sleep pipeline; those are a separate sleep-import input. |
| Weight and body fat | The separately enabled measurement importer reads HealthKit body mass, body-fat percentage, and waist circumference on iOS. Health Connect imports weight and body-fat records on Android. Imported records are associated with their platform identity in health_import_records; Health Connect updates a local measurement when its imported record changes, while the HealthKit importer skips identities already seen. Health Connect history can be limited to 30 days when permission is limited; otherwise its importer requests history from 1970. | Imported measurements appear in local measurement history and can be included in features that query measurement history, such as the TDEE estimator. This importer does not import Health Connect waist measurements. |
The Diary starts step and sleep refreshes while loading its health widgets. Those refreshes are throttled to about six hours unless forced; they are foreground app work, not a scheduled background health-sync job. Sleep import also applies its own six-hour minimum interval. Weight import can run on app cold start only after its import setting is enabled, and users can request an import from Health settings.
Exported data#
Export is separately enabled for Apple Health or Health Connect after availability and permission checks. In the current app flow, a foreground coordinator watches local measurement, nutrition, fluid, workout, and set-log table changes, debounces them for two seconds, and exports to the platform for the current operating system. It also schedules a run at startup. The user can trigger export from Health settings. The code does not install a background worker for health export.
| Train Libre source | Exported platform record |
|---|---|
| Measurement sessions | Weight, body-fat percentage, and BMI. Values with unsupported types or units are skipped. Android Health Connect currently omits BMI in the export service. Waist measurements are not part of this export mapping. |
| Nutrition diary entries | Calories, protein, carbohydrate, fat, and available fiber, sugar, and sodium, scaled by the logged quantity. Salt is converted to sodium by dividing by 2.5. Health Connect sanitizes fields and can retry a write without optional fiber, sugar, and sodium fields. |
| Fluid entries | Logged volume as liters. |
| Completed workout logs | Positive-duration sessions, mapped to running, walking, cycling, yoga, or strength. The title and notes are carried as platform metadata/fields where supported. On Android, completed strength sets are also exported as ExerciseSegment entries inside the Health Connect exercise session, with repetitions, load, weightlifting type, and a zero-based set index within each exercise. Non-strength sets are omitted. The current iOS export writes the workout session without individual set segments. |
Only mapped, persisted source records are eligible. Nutrition export reads the food product values linked to the diary entry; entries without a resolvable product are skipped. Workout export maps type from routine name and notes using simple keywords and defaults to strength, so the platform category can be approximate. When Health Connect strength segments were added, existing exported workout sessions also became eligible for a one-time idempotent payload upgrade that reuses their record identities. Platform permissions differ: Android requests Health Connect write access for weight, body fat, nutrition, hydration, and exercise sessions. iOS requests HealthKit share access for weight, body fat, BMI, active energy, nutrition, hydration, and workouts; the current Train Libre payload does not write active-energy records.
Retries, edits, and checkpoints#
Exports run separately for measurement, nutrition/hydration, and workout domains, in batches of at most 1,000 records. Each source record has a stable source key and a fingerprint of its export payload. Before writing, Train Libre prepares a stable native record ID and next revision; after a successful native write, it commits that identity and fingerprint locally. An unchanged fingerprint is skipped. An edited record is exported as a later revision using the same external ID so the platform adapter can replace the earlier version. This is local retry/deduplication state, not a guarantee of exactly-once delivery if the platform write succeeds but the app stops before its local commit.
Successful domain timestamps are stored separately in local preferences. The first run for a domain loads its available history; later runs use the domain checkpoint with a one-second overlap. The fingerprint and stable native identity suppress redundant writes. A failed domain does not advance its success checkpoint, and a later run can retry it. Nutrition and hydration writes have per-record fallback after a batch failure; measurement and workout batches are retried on a later run if their batch write fails.
Privacy and limits#
The repository implementation sends health records between the Flutter app and the operating system’s native health API through local method channels. The Health Export success event reports only the feature key (apple_health_exported or health_connect_exported) at this call site; record values, identifiers, and counts are not passed as event properties. The app does not send these health records to a Train Libre server in the inspected import/export paths. Apple Health or Health Connect may have their own account and device synchronization behavior, which Train Libre does not control.
Imported and exported data remain subject to platform permission choices, platform availability, source-data completeness, and the mappings above. Revoking access, having no matching records, or a platform rejecting a data type can result in missing or partial local data/export. Step aggregation is a heuristic for overlap, and estimated workout categories are based on text matching. Health Sync itself does not validate the accuracy or clinical meaning of records supplied by a platform.