From 180f07e23c1affbb880dd4e6dd7b8ea2cf393a92 Mon Sep 17 00:00:00 2001 From: rzen Date: Fri, 19 Jun 2026 17:17:14 -0400 Subject: [PATCH] Reflect watch-forwarded workout progress on the phone immediately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ingestFromWatch now upserts the SwiftData cache directly after writing the file, instead of relying on the NSMetadataQuery observer — a same-process file overwrite doesn't reliably emit a modified event, so watch progress never reached open iPhone screens. iCloud Drive stays the source of truth (file written first); the observer re-applies idempotently if it fires. Claude-Session: https://claude.ai/code/session_018gg69MaUetDNzWzBXisfMV --- CHANGELOG.md | 4 ++++ Workouts/Sync/SyncEngine.swift | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f809dcb..fd03a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ All notable changes to this project are documented here. advances the set count on the iPhone and a finished set is never un-counted, and reopening an exercise jumps straight to the first unfinished set (skipping completed work/rest pairs) instead of snapping back to set 1. +- Fixed: progress made on the watch now updates open iPhone screens live. The + phone applies a watch-forwarded workout to its cache directly on receipt, instead + of waiting on an `NSMetadataQuery` event that a same-process file overwrite + doesn't reliably emit. - Starting a workout on the iPhone now launches the Apple Watch app straight into the session via HealthKit (a one-time Health permission); the watch holds an `HKWorkoutSession` to stay active while you train and releases it when the diff --git a/Workouts/Sync/SyncEngine.swift b/Workouts/Sync/SyncEngine.swift index 7690a5f..34b746e 100644 --- a/Workouts/Sync/SyncEngine.swift +++ b/Workouts/Sync/SyncEngine.swift @@ -112,10 +112,20 @@ final class SyncEngine { onCacheChanged?() } - /// Apply a workout received from the watch through the normal write path - /// (file → observer → cache), keeping iCloud Drive the single source of truth. + /// Apply a workout received from the watch. iCloud Drive stays the source of + /// truth (we write the file), but we also upsert the cache directly here. + /// + /// The phone's own edits drive a local view copy, so they don't need this — but a + /// watch-originated change has nothing else refreshing the phone UI, and a + /// same-process file overwrite doesn't reliably wake the `NSMetadataQuery` + /// observer. Upserting the doc we just wrote keeps cache and file consistent (the + /// observer re-applies idempotently if it does fire) and lets open phone screens + /// reflect watch progress live. func ingestFromWatch(_ doc: WorkoutDocument) async { await save(workout: doc) + CacheMapper.upsertWorkout(doc, relativePath: doc.relativePath, into: context) + try? context.save() + onCacheChanged?() } // MARK: - Public CRUD (write path: files only)