Stabilize iOS/watchOS/tvOS apps and add cross-platform audit remediation

This commit is contained in:
Trey t
2026-02-11 12:54:40 -06:00
parent e40275e694
commit acce712261
77 changed files with 2940 additions and 765 deletions

View File

@@ -0,0 +1,35 @@
# Steps 1-5 Stabilization Deliverables
This repo now includes:
1. `SharedCore` Swift package with cross-platform utilities and dedicated test targets:
- `SharedCoreiOSTests`
- `SharedCoreWatchOSTests`
- `SharedCoreTVOSTests`
2. Auth token lifecycle protections in shared iOS/tvOS user/network code:
- token sanitization
- JWT expiry checks
- proactive refresh trigger when near expiry
- forced logout on `401`/`403`
3. Smoke scripts in `scripts/smoke/` for iOS/watchOS/tvOS plus package tests.
4. Runtime logging hooks (structured `os.Logger`) in network/auth/datastore/watch bridge/workout paths.
5. CI workflow `.github/workflows/apple-platform-ci.yml` that runs the smoke suite.
6. Build warning cleanup:
- disabled AppIntents metadata extraction for iOS/watchOS/tvOS targets that do not link `AppIntents`.
## SharedCore wiring
- `SharedCore` is linked as a local Swift package product to:
- `Werkout_ios` (iOS)
- `Werkout_watch Watch App` (watchOS)
- `WekoutThotViewer` (tvOS)
- Shared helpers are actively used in app code:
- `TokenSecurity` now drives token sanitization/expiry/rotation checks in `UserStore`.
- `RuntimeReporter` now handles network/auth/datastore runtime error reporting.
## Local commands
```bash
./scripts/ci/scan_tokens.sh
./scripts/smoke/smoke_all.sh
```

View File

@@ -0,0 +1,39 @@
# Step 10 Reliability Round 2
## Coverage
- Reviewed and patched:
- `iphone/Werkout_ios/BridgeModule+Watch.swift`
- `iphone/Werkout_ios/BridgeModule+WorkoutActions.swift`
- `iphone/Werkout_ios/CurrentWorkoutInfo.swift`
- `iphone/Werkout_watch Watch App/WatchDelegate.swift`
- `iphone/Werkout_ios/UserStore.swift`
- Validation:
- `./scripts/smoke/smoke_all.sh`
- iOS/tvOS analyzer passes
## Fixes
1. Main-thread state safety for watch session callbacks
- Wrapped `didReceiveMessageData` action handling and `activationDidCompleteWith` state transitions on main.
- Prevents shared bridge state (`@Published` workout/watch properties + queued message mutations) from being changed off-main.
2. Removed dead closure path that could retain `BridgeModule`
- Removed unused `CurrentWorkoutInfo.complete` closure and its assignment in `BridgeModule+WorkoutActions.start(workout:)`.
- Reduces lifecycle risk and removes dead behavior.
3. HealthKit authorization crash hardening on watch launch
- Replaced force-unwrapped quantity types with guarded optional binding in `WatchDelegate`.
- Logs and exits cleanly if required HealthKit quantity types are unavailable.
4. Cross-target notification compile stability
- Updated `UserStore.logout` to post `Notification.Name("CreatedNewWorkout")` directly.
- Avoids reliance on an iOS-only extension file when `UserStore` is compiled in tvOS target.
## Validation
- Smoke suite passed:
- token scan
- SharedCore tests
- iOS/watchOS/tvOS builds

View File

@@ -0,0 +1,40 @@
# Step 11 Watch Regression + Architecture Cleanup
## Scope
Executed in requested order:
1. `#2` Focused watch/phone disconnect-reconnect regression coverage.
2. `#3` Architecture cleanup to reduce shared cross-target coupling.
## #2 Regression Work
- Added shared queue primitive:
- `SharedCore/Sources/SharedCore/BoundedFIFOQueue.swift`
- Added regression tests for disconnect/reconnect replay and overflow behavior:
- `SharedCore/Tests/SharedCoreWatchOSTests/BoundedFIFOQueueTests.swift`
- Wired iOS watch bridge queueing to shared queue:
- `iphone/Werkout_ios/BridgeModule.swift`
- `iphone/Werkout_ios/BridgeModule+Watch.swift`
- Wired watch sender queueing to shared queue:
- `iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift`
## #3 Architecture Cleanup
- Replaced ad-hoc notification wiring with a shared typed notification constant:
- `SharedCore/Sources/SharedCore/AppNotifications.swift`
- Updated consumers to use shared constant:
- `iphone/Werkout_ios/UserStore.swift`
- `iphone/Werkout_ios/Werkout_iosApp.swift`
- `iphone/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift`
- `iphone/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift`
- Removed iOS-only notification extension that created cross-target coupling:
- `iphone/Werkout_ios/Extensions.swift`
## Validation
- `./scripts/smoke/smoke_all.sh` passed:
- token scan
- SharedCore tests (including new queue tests)
- iOS/watchOS/tvOS builds

View File

@@ -0,0 +1,53 @@
# Step 12 Hardware Disconnect/Reconnect Pass
## Date
- 2026-02-11 (UTC timestamp used during check: `2026-02-11T18:45:42Z`)
## Coverage Attempted
- Device inventory:
- `xcrun xcdevice list`
- iOS destination eligibility:
- `xcodebuild -project iphone/Werkout_ios.xcodeproj -scheme 'Werkout_ios' -showdestinations`
- watchOS destination eligibility:
- `xcodebuild -project iphone/Werkout_ios.xcodeproj -scheme 'Werkout_watch Watch App' -showdestinations`
## Findings
1. Hardware watch pass could not be executed from this environment.
- Evidence:
- watch scheme showed simulator destinations only under available destinations.
- the only physical watch destination was ineligible:
- `Hollies Apple Watch` with error indicating watchOS version mismatch/unknown against deployment target `watchOS 9.4`.
2. Physical iOS devices are present/eligible.
- Evidence:
- available iOS hardware destinations included:
- `Gary Tartts iPad`
- `ihollieb`
- `Peeeeeeeeellll`
3. Physical watch pairing/connectivity state is the blocking dependency for true hardware disconnect/reconnect validation.
## What Was Added For Immediate Re-Run
- Hardware run script:
- `scripts/hardware/watch_disconnect_hardware_pass.sh`
- Script behavior:
- validates eligible physical iOS + watch destinations
- performs hardware-targeted preflight builds (`CODE_SIGNING_ALLOWED=NO`)
- emits manual disconnect/reconnect checklist and pass criteria artifact
## Manual Hardware Scenario (Pending Once Watch Is Eligible)
1. Start iOS + watch apps on physical paired devices.
2. Start workout from iOS.
3. Break transport (Bluetooth off on iPhone or Airplane Mode on watch) for ~30s.
4. While disconnected, trigger multiple state changes on iOS.
5. Reconnect transport and verify watch converges to latest state without crash/replay loop.
6. Repeat for two disconnect/reconnect cycles.
## Current Status
- Blocked on eligible physical watch destination.
- Queue/replay behavior is covered by automated tests in `SharedCore/Tests/SharedCoreWatchOSTests/BoundedFIFOQueueTests.swift`, but physical transport behavior remains unverified until watch eligibility is fixed.
- In this Codex shell, scripted destination probing/building is additionally constrained by sandboxed write restrictions to Xcode/SwiftPM cache paths; manual run on your local terminal is expected once watch hardware is eligible.

View File

@@ -0,0 +1,55 @@
# Step 6 Audit Round 1 (P0/P1)
## Coverage
- Reviewed high-risk auth/session/network/watch files:
- `iphone/Werkout_ios/UserStore.swift`
- `iphone/Werkout_ios/Network/Network.swift`
- `iphone/Werkout_ios/BridgeModule+Watch.swift`
- `iphone/Werkout_watch Watch App/WatchMainViewModel.swift`
- `iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift`
- `iphone/Werkout_ios/HealthKitHelper.swift`
- `iphone/Werkout_ios/CurrentWorkoutInfo.swift`
- Ran:
- `./scripts/smoke/smoke_all.sh`
- Added/ran regression tests in `SharedCore` for token lifecycle and watch payload validation.
## Findings And Fixes
1. `P1` Watch command loss during activation
- Evidence: `iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift:40`
- Problem: payloads were dropped when `WCSession` was not activated.
- Fix: added bounded queue (`maxQueuedPayloads`), enqueue on inactive session, flush on activation.
2. `P1` Silent/unsafe watch payload decode failures
- Evidence: `iphone/Werkout_ios/BridgeModule+Watch.swift:73`
- Evidence: `iphone/Werkout_watch Watch App/WatchMainViewModel.swift:74`
- Problem: `try?` decode silently ignored malformed payloads.
- Fix: added shared `WatchPayloadValidation` with size checks and structured decode failures; both decode paths now reject+log bad payloads.
3. `P1` Auth token normalization gap for prefixed tokens
- Evidence: `SharedCore/Sources/SharedCore/TokenSecurity.swift:24`
- Problem: `"Token ..."` / `"Bearer ..."` values were not normalized.
- Fix: normalize known auth prefixes and reject bare prefix-only strings.
4. `P1` Network reliability/threading risk
- Evidence: `iphone/Werkout_ios/Network/Network.swift:12`
- Problem: infinite request timeouts and completion handlers returning on background threads.
- Fix: finite timeout (`30s`) and centralized main-thread completion delivery.
5. `P1` HealthKit helper shared mutable-state race
- Evidence: `iphone/Werkout_ios/HealthKitHelper.swift:20`
- Problem: mutable cross-request state (`completion`, counters, shared result object) could race and mis-route results.
- Fix: per-request aggregation via `DispatchGroup`, single UUID query (`limit: 1`), thread-safe aggregation queue, structured runtime logging.
6. `P2` Workout order inconsistency across helpers
- Evidence: `iphone/Werkout_ios/CurrentWorkoutInfo.swift:24`
- Problem: some paths used unsorted `workout.supersets` while others used sorted supersets.
- Fix: unified core navigation/lookup paths on sorted `superset` accessor and corrected bounds check.
## Validation
- Smoke suite passed after fixes:
- token scan
- SharedCore tests (including new regression tests)
- iOS/watchOS/tvOS builds

View File

@@ -0,0 +1,62 @@
# Step 7 UI/State/Accessibility Round 1
## Coverage
- Reviewed and patched high-traffic iOS/watchOS UI paths:
- workout browsing, planned workouts, workout detail, create-workout flow, login, watch main view
- Ran validation:
- `./scripts/smoke/smoke_all.sh`
## Fixes Applied
1. Create workout state consistency and duplicate-submit prevention
- `iphone/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift`
- Added `isUploading` gate, title trimming validation, shared `WorkoutValidation` integration.
2. Weight stepper logic bug
- `iphone/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift`
- Fixed weight decrement mismatch (`-15` to `-5`) to match increment behavior.
3. Create-workout UX cleanup and accessibility
- `iphone/Werkout_ios/Views/CreateWorkout/CreateWorkoutMainView.swift`
- Replaced visible sentinel row with hidden spacer, disabled upload button while uploading, added button labels/hints.
4. Superset editing accessibility/state
- `iphone/Werkout_ios/subview/CreateWorkoutSupersetView.swift`
- Avoided sheet toggle race by setting `showAddExercise = true`; added accessibility labels/hints.
5. Exercise action controls accessibility
- `iphone/Werkout_ios/Views/CreateWorkout/CreateExerciseActionsView.swift`
- Added accessibility labels to steppers and icon-only controls.
6. Workout list/planned list row accessibility
- `iphone/Werkout_ios/Views/AllWorkouts/AllWorkoutsListView.swift`
- `iphone/Werkout_ios/subview/PlannedWorkoutView.swift`
- Converted tap-only rows to plain `Button`s for VoiceOver/focus reliability.
7. Workout detail list ordering/scroll stability
- `iphone/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift`
- Aligned list ordering with sorted superset order and introduced stable row IDs for consistent scroll targeting.
8. Workout detail control accessibility + progress text guard
- `iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift`
- Added accessibility labels to icon-only controls and avoided negative progress display.
9. Login form input/accessibility improvements
- `iphone/Werkout_ios/Views/Login/LoginView.swift`
- Added keyboard/input autocorrection settings and accessibility labels/hints.
10. HealthKit auth safety/logging in all-workouts screen
- `iphone/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift`
- Removed force-unwrapped HK types and added runtime warning on failed authorization.
11. watchOS no-workout screen and accessibility polish
- `iphone/Werkout_watch Watch App/MainWatchView.swift`
- Replaced emoji placeholder with clear status text/icon and added combined accessibility labels.
## Validation
- Smoke suite passed after fixes:
- token scan
- SharedCore tests
- iOS/watchOS/tvOS builds

View File

@@ -0,0 +1,48 @@
# Step 8 Performance/State Round 1
## Coverage
- Reviewed and patched:
- `iphone/Werkout_ios/DataStore.swift`
- `iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift`
- `iphone/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift`
- `WekoutThotViewer/WekoutThotViewer/ContentView.swift`
- `iphone/Werkout_ios/BridgeModule.swift`
- `iphone/Werkout_ios/BridgeModule+Watch.swift`
- `iphone/Werkout_ios/BridgeModule+Timer.swift`
- `iphone/Werkout_watch Watch App/WatchWorkout.swift`
- Validation run:
- `./scripts/smoke/smoke_all.sh`
## Fixes
1. Coalesced concurrent `fetchAllData` requests
- `DataStore` now queues completion handlers while a fetch is active to prevent overlapping network fan-out and state churn.
2. Reduced AVPlayer churn in iOS workout detail
- Reuses current player for same URL by seeking to start instead of recreating `AVPlayer` each time exercise/video updates.
3. Reduced AVPlayer churn in iOS exercise preview sheet
- Added preview URL tracking; same URL now replays without allocating a new player.
4. Reduced AVPlayer churn in tvOS content loop
- Same URL replay now seeks/replays existing player rather than recreating.
5. Capped queued watch messages on iOS bridge
- Added queue cap to prevent unbounded growth while watch is disconnected.
6. Added queue fallback for send failures
- Failed reachable send now re-queues payload for later delivery.
7. Improved timer power behavior
- Added timer tolerance to workout/exercise timers.
8. Fixed watch heart-rate loop early-return behavior
- Non-heart sample types now `continue` instead of exiting handler early.
## Validation
- Smoke suite passed:
- token scan
- SharedCore tests
- iOS/watchOS/tvOS builds

View File

@@ -0,0 +1,39 @@
# Step 9 Memory/Lifecycle Round 1
## Coverage
- Audited lifecycle cleanup and resource teardown in:
- `iphone/Werkout_ios/subview/PlayerUIView.swift`
- `iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift`
- `iphone/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift`
- `iphone/Werkout_ios/Views/ExternalWorkoutDetailView.swift`
- `iphone/Werkout_ios/subview/AllExerciseView.swift`
- `iphone/Werkout_ios/Views/CreateWorkout/CreateExerciseActionsView.swift`
- `iphone/Werkout_ios/AudioEngine.swift`
- `WekoutThotViewer/WekoutThotViewer/ContentView.swift`
- Validation:
- `./scripts/smoke/smoke_all.sh`
## Fixes
1. Player view teardown safety
- `PlayerView` now pauses previous players when swapping and performs explicit teardown in `dismantleUIView`.
2. Workout detail closure retention risk
- Clears `BridgeModule.shared.completedWorkout` on `WorkoutDetailView` disappear.
3. Player pause on dismiss across views
- Added `onDisappear` player pause in workout detail exercise list, create-exercise preview, all-exercise preview, external display, and tvOS content view.
4. External display player reuse
- Added URL tracking + replay path to avoid reallocating AVPlayer when URL is unchanged.
5. Audio playback resource churn
- Stops existing players before replacement and logs failures via `RuntimeReporter` instead of `print`.
## Validation
- Smoke suite passed:
- token scan
- SharedCore tests
- iOS/watchOS/tvOS builds