# SwiftUI HealthKit Sleep Chart Template: Build It Right

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-04. 6 min read.
> Source: https://vp0.com/blogs/swiftui-healthkit-sleep-chart-template

HealthKit gives you stages and intervals; Swift Charts draws them. The tricky part is authorization, not the chart.

**TL;DR.** A SwiftUI sleep chart reads HKCategoryType sleepAnalysis samples from HealthKit and plots them with Swift Charts, usually as stacked bars per night or a stage timeline. The two things people get wrong are authorization (you must add the HealthKit capability plus an Info.plist usage string, and read access is intentionally opaque, so never assume denial means no data) and aggregation (sleep arrives as overlapping intervals you must merge per night). Start from a clean charting layout and wire the real HKHealthStore query into it. A free VP0 design gives you that layout at $0.

A SwiftUI sleep chart is two pieces that people tend to mix up: a [HealthKit](https://developer.apple.com/documentation/healthkit) query that returns sleep samples, and a [Swift Charts](https://developer.apple.com/documentation/charts) view that draws them. The chart is the easy half. The hard half is authorization and aggregation, because sleep does not arrive as one number per night. Here is the data model, the gotchas that waste an afternoon, and how to start from a clean template. To skip the layout work, begin from a free [VP0](https://vp0.com) design (the free iOS and React Native design library AI builders read from) and wire the real query into it at $0.

## The data model: sleep is intervals, not a number

Sleep lives in HealthKit as a category sample under [`HKCategoryTypeIdentifier.sleepAnalysis`](https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/sleepanalysis). Each sample is a start date, an end date, and a value: `inBed`, `awake`, or one of the asleep stages (`asleepCore`, `asleepDeep`, `asleepREM`, and `asleepUnspecified`) on modern iOS. A single night is many overlapping samples, sometimes from several sources (the Watch, the phone, a third-party tracker). So your first job is not charting, it is merging: group samples by night, deduplicate overlaps, and total time per stage. Only then do you have something to plot.

## Authorization, the part that actually trips people

Two settings are mandatory before any query runs. Add the HealthKit capability to the target, and add an `NSHealthShareUsageDescription` string to Info.plist explaining why you read health data. Miss either and the authorization request throws.

The real surprise is privacy by design: HealthKit makes **read** authorization opaque. Your app cannot tell whether the user granted read access or simply has no sleep recorded, so an empty result is never proof of a denial. Treat empty as a normal, designed-for state with its own UI, not an error. This matters for the chart because a blank chart and a not-yet-authorized chart should look different to the user even though your code sees the same empty array.

## Building the chart with Swift Charts

Once you have per-night aggregates, two layouts cover most apps:

| Chart style | Swift Charts marks | Good for |
|---|---|---|
| Nightly total bars | `BarMark` per night, x = date, y = hours | Trend over a week or month |
| Stage breakdown | stacked `BarMark` with `foregroundStyle(by:)` on stage | One night, deep vs REM vs core |
| Stage timeline | `RectangleMark` across start and end per stage | A detailed single-night view |

Drive the chart from a small `@Observable` view model that holds the aggregated nights, so the view stays declarative and you can preview it with sample data. Keep the `HKHealthStore` query out of the view entirely; that separation is what makes the template testable and reusable. For the long-running query, prefer `HKSampleQueryDescriptor` on current iOS, and request a sensible date window rather than all history.

## Start from a clean template

The fastest reliable path is to start from a layout that already has the chart, the legend, and the empty state, then drop your real query in. A clean base also keeps the AI honest when you generate the query code in Cursor or Claude Code. For adjacent HealthKit work, see [the Apple HealthKit step counter SwiftUI template](/blogs/apple-healthkit-step-counter-swiftui-template/) and [the cold plunge timer with HealthKit sync](/blogs/cold-plunge-timer-healthkit-sync-ui-swiftui/); for a non-charting health screen, [the mental health journal app in SwiftUI](/blogs/mental-health-journal-app-swiftui/) shows the same authorization pattern. If you also build cross-platform, the [iOS skeleton loaders in React Native](/blogs/ios-skeleton-loaders-ui-react-native/) covers the loading state while the query runs, and [the React Native RTL flexbox fix](/blogs/react-native-rtl-flexbox-layout-fix-ai/) handles localized layouts. Keep all health copy descriptive and follow the [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/) so the chart reads as information, not a diagnosis.

The estimate-grade dashboard these markers often feed, framed honestly, is covered in [the biological age dashboard guide](/blogs/biological-age-calculator-dashboard-ui-ios/).

The same read-permission opacity and interval patterns carry into [the intermittent fasting timer ring](/blogs/apple-healthkit-intermittent-fasting-timer-ring/), where HealthKit supplies the correlation layer for a log it cannot store.

## Key takeaways

- Sleep is `sleepAnalysis` category samples: overlapping intervals you must merge per night.
- Add the HealthKit capability and an `NSHealthShareUsageDescription` string before any query.
- Read authorization is opaque by design, so empty is not the same as denied; design for it.
- Render with Swift Charts (`BarMark` for totals, stacked or `RectangleMark` for stages).
- Keep the query out of the view, and start from a clean VP0 chart layout at $0.

## Frequently asked questions

### How do I build a sleep chart in SwiftUI with HealthKit?

Request read authorization for the HKCategoryType sleepAnalysis identifier, run an HKSampleQuery (or HKSampleQueryDescriptor) over a date range, merge the overlapping intervals into per-night totals or stages, and render them with Swift Charts using BarMark for nightly totals or a timeline of RuleMark and RectangleMark for stages. Add the HealthKit capability and an NSHealthShareUsageDescription string first, or the request fails.

### What HealthKit type holds sleep data?

Sleep is a category sample under HKCategoryTypeIdentifier.sleepAnalysis. Each sample has a start date, an end date, and a value such as inBed, asleepCore, asleepDeep, asleepREM, or awake on modern iOS. You read intervals, not a single nightly number, so you aggregate them yourself.

### Why does my HealthKit read return no data?

Apple makes read authorization opaque on purpose for privacy: your app cannot tell whether the user granted read access or simply has no data, so an empty result is not the same as a denial. Confirm there is sleep data in the Health app, check that your usage-description string and capability are set, and handle the empty case in the UI instead of treating it as an error.

### Can a HealthKit sleep app give medical advice?

No. HealthKit is a data store, not a clinical device, and an app that reads sleep samples is not a diagnostic tool. Show the data and trends, avoid medical claims or diagnoses, and follow Apple's HealthKit and App Review rules. If you make health claims you take on regulatory obligations that a charting template does not cover.

### What is the best template for a SwiftUI HealthKit sleep chart?

One that separates the HealthKit query from the chart view so you can test each. A free VP0 design, the free iOS and React Native design library for AI builders, gives a clean Swift Charts layout you can drop a real HKHealthStore query into and generate in Cursor or Claude Code at $0.

## Frequently asked questions

### How do I build a sleep chart in SwiftUI with HealthKit?

Request read authorization for the HKCategoryType sleepAnalysis identifier, run an HKSampleQuery (or HKSampleQueryDescriptor) over a date range, merge the overlapping intervals into per-night totals or stages, and render them with Swift Charts using BarMark for nightly totals or a timeline of RuleMark and RectangleMark for stages. Add the HealthKit capability and an NSHealthShareUsageDescription string first, or the request fails.

### What HealthKit type holds sleep data?

Sleep is a category sample under HKCategoryTypeIdentifier.sleepAnalysis. Each sample has a start date, an end date, and a value such as inBed, asleepCore, asleepDeep, asleepREM, or awake on modern iOS. You read intervals, not a single nightly number, so you aggregate them yourself.

### Why does my HealthKit read return no data?

Apple makes read authorization opaque on purpose for privacy: your app cannot tell whether the user granted read access or simply has no data, so an empty result is not the same as a denial. Confirm there is sleep data in the Health app, check that your usage-description string and capability are set, and handle the empty case in the UI instead of treating it as an error.

### Can a HealthKit sleep app give medical advice?

No. HealthKit is a data store, not a clinical device, and an app that reads sleep samples is not a diagnostic tool. Show the data and trends, avoid medical claims or diagnoses, and follow Apple's HealthKit and App Review rules. If you make health claims you take on regulatory obligations that a charting template does not cover.

### What is the best template for a SwiftUI HealthKit sleep chart?

One that separates the HealthKit query from the chart view so you can test each. A free VP0 design, the free iOS and React Native design library for AI builders, gives a clean Swift Charts layout you can drop a real HKHealthStore query into and generate in Cursor or Claude Code at $0.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
