Build a WHOOP-Style Recovery Score Dial in SwiftUI
A recovery dial is a circular gauge, but the score behind it is a derived metric. Here is how to build the WHOOP-style dial in SwiftUI.
TL;DR
A WHOOP-style recovery dial is a circular gauge that shows a recovery score from 0 to 100% with red, yellow, and green zones and the number in the center. The dial itself is straightforward in SwiftUI, a Gauge or a custom trimmed arc with an animated fill, but the score behind it is a derived wellness metric computed from inputs like heart-rate variability, resting heart rate, and sleep, read from HealthKit with permission, not a value the dial invents. A free VP0 recovery-dial template gives an agent the gauge, the zones, and the states to extend, while you wire the score. It is a wellness display, not medical advice.
What a recovery dial really is
A WHOOP-style recovery dial is a circular gauge: an arc that fills from zero to a percentage, banded into red, yellow, and green zones, with the score in the center and a short label beneath. It reads at a glance, which is the point, a single number that says how recovered the body is today. The dial itself is the easy half, a shape you draw and animate. The harder and more important half is the score it displays, because a recovery dial is only as meaningful as the metric behind it, and that metric is computed, not invented by the gauge.
Separating the two keeps the project honest. You can build a beautiful dial in an afternoon, but a dial showing a made-up number is worse than no dial, so the score’s source matters as much as the visual.
Where the score comes from
The recovery score is a derived wellness metric, computed from physiological inputs rather than read off a sensor directly. The common inputs are heart-rate variability, resting heart rate, and sleep, which on iOS come from HealthKit with the user’s permission, and a scoring model combines them into a single percentage. The dial then displays that percentage; it does not generate it. This matters for honesty as much as accuracy: the app should be transparent that the score is a computed estimate from those inputs, not a medical reading, and it should never present a recovery percentage as a diagnosis.
So the build has two clear parts. The score computation and the HealthKit access are the data layer, owned by you and your model, and the dial is the presentation. Confusing the two, or implying the dial measures something it only displays, is the mistake to avoid.
Building the dial: the realistic options
There are three ways to draw the dial, and they trade control for effort.
| Approach | Zone-band control | Animation | Effort |
|---|---|---|---|
| SwiftUI Gauge | Limited styling of the colored zones | Built in but constrained | Low |
| Custom arc with a trimmed stroke | Full control of zones, colors, and caps | Smooth and fully custom | Medium, the production answer |
| Chart library gauge | Varies, often opaque | Usually good | Low, with less control |
SwiftUI’s Gauge gives you a circular gauge quickly, and for a simple dial it is enough, but styling the colored recovery zones and the exact look of the arc is constrained. A custom arc, a Circle trimmed to the score’s fraction with a rounded stroke over a zoned background ring, gives full control of the colors, the caps, and the animation, which is why a polished recovery dial usually uses one. A chart library can also draw a gauge but tends to be a black box that resists the specific zone styling a recovery dial wants. The visual guidance in Apple’s charting data guidelines applies either way. A free VP0 recovery-dial template starts you on the custom-arc version, with the zoned ring, the animated fill, the centered score, and the states already shaped and exposed through a machine-readable source page, so an agent like Cursor or Claude Code extends a real dial and you wire the score. The surrounding health-data patterns appear in a CGM glucose chart, and the wearable shell in an Apple Watch app UI kit.
Animating the dial so it feels alive
A recovery dial earns its impact through animation. When the score loads, the arc should sweep up to its value rather than appearing instantly, and the color should match the zone the score lands in, a 33% recovery in the red band, a 75% in green. The number in the center can count up alongside the arc so the two move together. The sweep runs smoothly so the dial feels like a considered readout rather than a static label, and a subtle pulse or glow at rest can signal the day’s state without animation that distracts. These touches are small but they are what make a recovery dial feel like the centerpiece of a screen, which it usually is.
Keep the animation honest to the data, though. The arc should land exactly on the computed score, not overshoot for drama, because a recovery dial is a number people make decisions with, and a misleading sweep undermines trust.
The states a recovery dial needs
A recovery dial needs the states a real health screen hits. A loading state covers the moment the score is being computed or HealthKit is being read, so the dial does not show a stale or zero value as if it were real. A no-data state handles a user who has not granted HealthKit access or has not worn a device long enough to produce a score, with a clear explanation rather than a blank dial. The zones need legible thresholds so a score near a boundary is not ambiguous, and the dial should be accessible, with the score available to VoiceOver and not conveyed by color alone. The Apple Watch surface that often pairs with it shares patterns with an Apple Watch Ultra action button UI.
These states are where a health app shows its care. A dial that shows a confident number when it has no real data, or that hides behind color for colorblind users, fails the people relying on it.
Keeping it honest: a recovery score is not a diagnosis
A recovery dial presents a wellness estimate, and it should be clear about what it is not. The score is a computed signal from heart-rate variability, resting heart rate, and sleep, useful for guiding training and rest, but it is not a medical measurement and not a diagnosis. So the app should describe the score as an estimate, avoid medical claims, and never tell a user they are healthy or unwell based on a percentage. Where the score is low, the helpful framing is rest and recovery, not alarm, and anything that looks like a medical concern belongs with a clinician, not a dial. This is the same care any health surface owes its users.
Keeping that line clear is part of building a trustworthy fitness app. A recovery dial that overstates what it measures, or implies medical authority, is a different and riskier product than one that honestly displays a wellness estimate.
Key takeaways: a recovery score radial dial
- The dial is presentation; the score is data. Build the gauge, but the percentage is computed, not invented.
- The score is derived from HealthKit inputs. Heart-rate variability, resting heart rate, and sleep, with permission.
- A custom arc gives the most control. A trimmed stroke over a zoned ring beats a constrained Gauge for a polished dial.
- Animate to the real value. Sweep the arc to the exact score, match the zone color, and keep it honest.
- Start from a recovery-dial template. A free VP0 template gives an agent the zoned dial and states to wire a score into.
What to choose
For a recovery dial, build the gauge from a template designed for it, usually a custom arc over a zoned ring, because the colored zones, the animation, and the states are where a polished dial differs from a generic one. A free VP0 recovery-dial template gives you the zoned arc, the animated fill, the centered score, and the loading and no-data states, so an agent extends a real dial and you wire the score computation and HealthKit, keeping the dial a wellness display rather than a medical claim. SwiftUI’s Gauge is a fine quick option for a simple dial, and a chart library works if you accept less control, but the score and its honest framing matter more than the rendering choice.
Frequently asked questions
How do I build a WHOOP-style recovery score radial dial in SwiftUI? Draw a circular gauge, most cleanly as a custom arc: a background ring banded into red, yellow, and green zones, with a Circle trimmed to the score’s fraction and a rounded stroke on top, the score number centered, and a label beneath. Animate the arc sweeping up to the value when it loads and match the color to the zone. The dial only displays the score, which is a derived metric computed from HealthKit inputs, so build the gauge and wire the score separately. A free recovery-dial template gives you the zoned arc, the animation, and the states to start from.
Where does the recovery score come from? It is a derived wellness metric, computed from physiological inputs rather than read directly off a sensor. The common inputs are heart-rate variability, resting heart rate, and sleep, which on iOS come from HealthKit with the user’s permission, and a scoring model combines them into a single percentage. The dial displays that percentage but does not generate it. Because it is a computed estimate from those inputs, the app should present it as a wellness signal rather than a medical reading, and it should be transparent about what feeds the score.
Where can I get a recovery dial or radial gauge template for SwiftUI? The most useful option is a template built for the zoned dial, not a generic gauge. A free VP0 recovery-dial template provides the zoned arc, the animated fill, the centered score, and the loading and no-data states, with a machine-readable source page, so an agent like Cursor or Claude Code extends a real dial. You then wire the score computation and HealthKit access, since the template is the presentation and the data is yours. It is built for the colored zones and honest states a recovery dial needs rather than a plain progress ring.
Is a recovery score medical advice? No, a recovery score is a computed wellness estimate from inputs like heart-rate variability, resting heart rate, and sleep, useful for guiding training and rest, but it is not a medical measurement or a diagnosis. A trustworthy app describes the score as an estimate, avoids medical claims, and never tells a user they are healthy or unwell based on a percentage. A low score is framed as a prompt to rest, not as alarm, and anything resembling a medical concern belongs with a clinician. Keeping that separation clear is part of building a fitness app responsibly.
How do I animate a recovery dial in SwiftUI? Animate the trimmed arc sweeping from zero up to the score’s fraction when the value loads, rather than snapping to it, and set the stroke color to the zone the score lands in. You can count the centered number up alongside the arc so they move together, and use a smooth, brief easing so the dial feels considered rather than flashy. Keep the animation honest by landing exactly on the computed score without overshooting, because the dial is a number people make decisions with, and a misleading sweep undermines trust.
Questions from the VP0 Vibe Coding community
How do I build a WHOOP-style recovery score radial dial in SwiftUI?
Draw a circular gauge, most cleanly as a custom arc: a background ring banded into red, yellow, and green zones, with a Circle trimmed to the score's fraction and a rounded stroke on top, the score number centered, and a label beneath. Animate the arc sweeping up to the value when it loads and match the color to the zone. The dial only displays the score, which is a derived metric computed from HealthKit inputs, so build the gauge and wire the score separately. A free recovery-dial template gives you the zoned arc, the animation, and the states to start from.
Where does the recovery score come from?
It is a derived wellness metric, computed from physiological inputs rather than read directly off a sensor. The common inputs are heart-rate variability, resting heart rate, and sleep, which on iOS come from HealthKit with the user's permission, and a scoring model combines them into a single percentage. The dial displays that percentage but does not generate it. Because it is a computed estimate from those inputs, the app should present it as a wellness signal rather than a medical reading, and it should be transparent about what feeds the score.
Where can I get a recovery dial or radial gauge template for SwiftUI?
The most useful option is a template built for the zoned dial, not a generic gauge. A free VP0 recovery-dial template provides the zoned arc, the animated fill, the centered score, and the loading and no-data states, with a machine-readable source page, so an agent like Cursor or Claude Code extends a real dial. You then wire the score computation and HealthKit access, since the template is the presentation and the data is yours. It is built for the colored zones and honest states a recovery dial needs rather than a plain progress ring.
Is a recovery score medical advice?
No, a recovery score is a computed wellness estimate from inputs like heart-rate variability, resting heart rate, and sleep, useful for guiding training and rest, but it is not a medical measurement or a diagnosis. A trustworthy app describes the score as an estimate, avoids medical claims, and never tells a user they are healthy or unwell based on a percentage. A low score is framed as a prompt to rest, not as alarm, and anything resembling a medical concern belongs with a clinician. Keeping that separation clear is part of building a fitness app responsibly.
How do I animate a recovery dial in SwiftUI?
Animate the trimmed arc sweeping from zero up to the score's fraction when the value loads, rather than snapping to it, and set the stroke color to the zone the score lands in. You can count the centered number up alongside the arc so they move together, and use a smooth, brief easing so the dial feels considered rather than flashy. Keep the animation honest by landing exactly on the computed score without overshooting, because the dial is a number people make decisions with, and a misleading sweep undermines trust.
Part of the Native Hardware, Sensors & Device Features hub. Browse all VP0 topics →
Keep reading
Smart Ring Sleep Tracker UI in SwiftUI
A free SwiftUI pattern for a smart ring sleep tracker: read sleep from HealthKit or a Bluetooth ring, show stages and readiness, and stay non-medical.
Video Editor Timeline UI in iOS (Learn the CapCut Pattern)
Build a video editor timeline UI in SwiftUI: scrubbable clips, trim handles, and layered tracks, from a free VP0 design. AVFoundation does the editing.
Apple HealthKit Step Counter in SwiftUI (Free Template)
Build a step-counter UI on HealthKit in SwiftUI: permission, today's steps, a trend chart, and goals, from a free VP0 design. Private, and not medical.
Police Scanner Radio App UI for iOS: The Honest Pattern
A police scanner app UI that works: geography-first feed directory, a sparse live player with truthful states, background audio, and the legal disclosures.
Biological Age Calculator Dashboard UI for iOS: Honest
Design a biological age dashboard: the estimate framed honestly, trends over absolutes, factor breakdowns tied to evidence, and zero longevity fear-mongering.
SwiftUI HealthKit Sleep Chart Template: Build It Right
Build a SwiftUI sleep chart from HealthKit sleep samples with Swift Charts. Here is the data model, the authorization gotcha, and a template to start from.