Skip to content

fix(ios): restore WHOOP 5/MG battery runtime after BLE relaunch - #146

Draft
kvnloo wants to merge 2 commits into
mainfrom
up/2887-ios-restored-whoop-5-0-mg-battery
Draft

kvnloo wants to merge 2 commits into
mainfrom
up/2887-ios-restored-whoop-5-0-mg-battery

Conversation

@kvnloo

@kvnloo kvnloo commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Problem

ryanbr/noop#2887: a WHOOP 5.0/MG restored after an iOS CoreBluetooth background relaunch can display a WHOOP 4.0's 108-hour battery-runtime fallback. Reconnects in the same process also lose the persisted discharge history until new samples accumulate.

Fixes ryanbr#2887

Root cause (base 2a1de7609843b095505274ad6fc811757210be80)

  • Strand/BLE/LiveState.swift:221 initializes batteryRatedHours to WHOOP 4.0 (108 h). Strand/BLE/BLEManager.swift:1659-1664 updated it only in connectCore, which the iOS willRestoreState callback at BLEManager.swift:6506-6524 bypasses.
  • LiveState.swift:765 clears battery samples on disconnect, while BLEManager.swift:1554-1571 seeded the 14-day store history only during bootstrapStore, which runs once per process.

Change

  • Derive the rated battery lifetime from selectedModel in one observer, including after both manager initializers. This also handles restore and scan-model changes without a separate connectCore assignment.
  • Keep a reference to the already-bootstrapped WhoopStore. Reuse its 14-day SoC read after each didConnect; drop a late read if the link has disconnected or the device identity changed. Retain existing timestamp deduplication and 400-sample live cap.
  • Apple BLE/app-only fix; Android already resolves the rated life from the detected family on each estimate read. No Android stored-data, analytics, UI, or scoring change, and no new localized UI strings.

Tests

NOT RUN — the fork has GitHub Actions disabled, and no workflow was dispatched. There is no exact-head CI receipt yet.

New StrandTests/RestoredBatteryEstimateTests.swift XCTest cases:

  1. Persisted WHOOP 5.0 / MG initializes its fallback at 288 h, even without connectCore.
  2. A synthetic in-memory WhoopStore with 100% at t - 10 h and 90% at t gives a 1%/h measured slope and 90 h = 3.75 days remaining; after clearBiometrics, reseeding restores exactly two samples and the measured estimate. A disconnected-only attempt seeds nothing; duplicate seeds remain two rows.

Hand-check of the user report: 0.91 × 108 = 98.28 h = 4.095 days (incorrect fallback), versus 0.91 × 288 = 262.08 h = 10.92 days (5/MG fallback). The 10-hour synthetic discharge exceeds the existing 2-hour/2%-drop slope gates. These are hand-worked expectations, not executed test outcomes.

Verify on a macOS 15+/Xcode host:

xcodegen generate
xcodebuild -project Strand.xcodeproj -scheme Strand -destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO -only-testing:StrandTests/RestoredBatteryEstimateTests test
xcodebuild -project Strand.xcodeproj -scheme NOOPiOS -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build

Exact upstream PR job names for these Strand/** + StrandTests/** paths: check (i18n Coverage), doc-comments (Source Hygiene), linux-capture (Tools Python CI), build (Strand, …) and build (NOOPiOS, …) (App build). No build-and-test, Swift package test (…), or tools (…) job is path-triggered.

Hardware needed

Still required: WHOOP 5.0/MG + iPhone. Reproduce a genuine Restore: CoreBluetooth relaunch (including already-connected restore), check that the 5/MG rated fallback remains ~12 days at 100%, reconnect within the same process and observe measured discharge restored from banked samples, and verify WHOOP 4.0 still uses 108 hours. Record model/firmware, redacted timestamps/SoC and estimate source without sharing personal health data.

Upstream readiness

HOLD pending exact-head macOS XCTest, iOS compile, independent review, and real-strap restoration evidence. The code and synthetic regressions are source-backed; no physical strap or iOS restoration path was exercised. Store access can fail while iOS data protection is locked; existing bootstrap retry/unlock behavior is unchanged. No upstream issue/PR/comment/review/reaction was created.

Implemented by ChatGPT (mobile app, GitHub connector) for Kevin's NOOP factory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS: restored WHOOP 5.0/MG battery estimate falls back to the WHOOP 4.0 rated life (108 h)

1 participant