Stabilize iOS/watchOS/tvOS apps and add cross-platform audit remediation
This commit is contained in:
35
docs/stabilization_steps_1_5.md
Normal file
35
docs/stabilization_steps_1_5.md
Normal 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
|
||||
```
|
||||
39
docs/step10_reliability_round2.md
Normal file
39
docs/step10_reliability_round2.md
Normal 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
|
||||
|
||||
40
docs/step11_watch_regression_and_architecture.md
Normal file
40
docs/step11_watch_regression_and_architecture.md
Normal 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
|
||||
|
||||
53
docs/step12_hardware_disconnect_pass.md
Normal file
53
docs/step12_hardware_disconnect_pass.md
Normal 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:
|
||||
- `Hollie’s 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 Tartt’s 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.
|
||||
55
docs/step6_audit_round1.md
Normal file
55
docs/step6_audit_round1.md
Normal 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
|
||||
62
docs/step7_ui_accessibility_round1.md
Normal file
62
docs/step7_ui_accessibility_round1.md
Normal 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
|
||||
48
docs/step8_performance_state_round1.md
Normal file
48
docs/step8_performance_state_round1.md
Normal 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
|
||||
39
docs/step9_memory_lifecycle_round1.md
Normal file
39
docs/step9_memory_lifecycle_round1.md
Normal 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
|
||||
Reference in New Issue
Block a user