Repository navigation
Description
Summary
ActivityFile.importNote() and ActivityFileImporter.summaryText use different tie-breaking rules above the 10 km display threshold. Swift formats with %.0f under nearest/ties-to-even; Kotlin uses roundToInt(), whose documented ties go toward positive infinity. At the exactly representable 10.5 km boundary Swift prints 10 km and Kotlin prints 11 km.
This is public Swift/Kotlin parity and honest-description drift. Stored distanceM remains unchanged.
Verified on meta032 032983d2bed9bf50dfcfc4d06ef707f3818cdc26 and current upstream main aa0037acbf7fc2ef972ba4676ae3e18d508be1ef.
Exact reproduction
Use otherwise minimal GPX / Running activities with no GPS, HR, steps, or average-HR suffixes.
| distanceM | formatter | Swift | Kotlin |
|---|---|---|---|
| 9500 | importNote |
Imported GPX · 9.50 km |
Imported GPX · 9.50 km |
| 10500 | importNote |
Imported GPX · 10 km |
Imported GPX · 11 km |
| 11500 | importNote |
Imported GPX · 12 km |
Imported GPX · 12 km |
| 9500 | summaryText |
Imported a 9.50 km Running activity |
Imported a 9.50 km Running activity |
| 10500 | summaryText |
Imported a 10 km Running activity |
Imported a 11 km Running activity |
| 11500 | summaryText |
Imported a 12 km Running activity |
Imported a 12 km Running activity |
The .5 values are exactly representable as binary Double; this is tie policy, not precision noise.
Production reachability
- Swift
DataSourcesViewwritesactivity.importNote()into persistedWorkoutRow.notes, then assignssummaryTextto visible completion text. - Kotlin
importExportpersists the note;WorkoutsScreenrenders Notes. It returnssummaryTextasImportSummary.message, whichDataSourcesScreendisplays in a Toast.
Thus importNote is durable descriptive drift and summaryText is immediate UI drift. No metric is corrupted.
Distinction from #91
#91 and upstream PR ryanbr#1477 concern only positive steps omitted from Swift summaryText. #91 explicitly says the separate per-workout note remains intact. PR ryanbr#1477 adds 1.00 km summary controls and the Swift step suffix; it touches neither distance formatter nor any rounding boundary.
Cause and provenance
Both implementations entered together in upstream commit 4b69da6fa0b950687d4268ce525a5033b3cb9835 (6.0.0 offline activity import, 2026-06-20): Swift uses String(format: "%.0f", km) while Kotlin uses km.roundToInt().
Severity and tests
P2: persisted and immediately visible descriptive output violates the explicit twin/honest-summary contract by 1 km at a reachable boundary. Parsed and persisted distance stays correct. No direct public tests on current main pin either formatter at 9.5/10.5/11.5 km; PR ryanbr#1477 only tests summaryText at 1.00 km for steps.
Duplicate audit
Open and closed issues and PRs in bhelm/noop and ryanbr/noop were searched for ActivityFileImporter rounding, summaryText distance, importNote, 10.5 km, activity import summary, distance rounding, FIT distance, imported km, and roundToInt String format activity. No exact duplicate exists.
Related but non-duplicate: fork #91 / upstream PR ryanbr#1477, upstream ryanbr#483/ryanbr#489, ryanbr#568/ryanbr#584, ryanbr#585/ryanbr#1058/ryanbr#1088, and ryanbr#1205/ryanbr#1238.
Upstream-only acceptance
- Choose and document one explicit cross-platform policy: for these nonnegative distances, round half up / ties away from zero, making 10.5 km exactly
11 km. - Add mirrored public tests for both
importNoteandsummaryTextat 9.5, 10.5, and 11.5 km. Canonical 10.5 output is11 kmon both platforms. - Preserve the two-decimal path below 10 km, field order, separators, sport normalization, suffix behavior, and exact
distanceM. - Implement Production only upstream from a fresh current-main branch; do not transplant Product into the fork harness.
- Native execution requires separate authorization.
Activity
bhelm commentedon Aug 21, 2026
Resolved upstream by ryanbr/noop#1499, merge commit 940ded8e, which is contained in current upstream
main. The merged fix includes regression coverage and passed upstream CI. Closing as completed.