
A clean, lightweight SwiftUI package for displaying beautiful sleep stage visualizations with comprehensive HealthKit integration.
- 📊 Timeline Visualization - Interactive sleep stage timeline with smooth stage transitions
- 🎨 Customizable Colors - Define your own color scheme for different sleep stages
- ⏰ Time Axis - Clear time labels showing sleep session duration
- đź“‹ Legend - Duration summary for each sleep stage
- 🏥 HealthKit Integration - Native support for
HKCategoryValueSleepAnalysis
data - 🌍 Localization Support - Configurable display names for internationalization
- đź”§ SOLID Architecture - Clean, testable, and extensible design
- 📱 Cross-Platform - iOS 15+, macOS 12+, watchOS 8+, tvOS 15+
Add SleepChartKit to your project via Xcode:
- File → Add Package Dependencies
- Enter:
https://github.com/DanielJamesTronca/SleepChartKit
- Select version and add to target
Or add to your Package.swift
:
dependencies: [
.package(url: "https://github.com/DanielJamesTronca/SleepChartKit", from: "1.0.0")
]
import SwiftUI
import SleepChartKit
struct ContentView: View {
let sleepSamples = [
SleepSample(stage: .asleepDeep, startDate: date1, endDate: date2),
SleepSample(stage: .asleepCore, startDate: date2, endDate: date3),
SleepSample(stage: .asleepREM, startDate: date3, endDate: date4),
SleepSample(stage: .awake, startDate: date4, endDate: date5)
]
var body: some View {
SleepChartView(samples: sleepSamples)
.padding()
}
}
The circular chart displays sleep duration as a percentage of a configurable threshold, starting from the top (12 o'clock) and filling clockwise:
import SwiftUI
import SleepChartKit
struct CircularChartView: View {
let sleepSamples = [
SleepSample(stage: .asleepDeep, startDate: date1, endDate: date2),
SleepSample(stage: .asleepCore, startDate: date2, endDate: date3),
SleepSample(stage: .asleepREM, startDate: date3, endDate: date4)
]
var body: some View {
VStack(spacing: 30) {
// Basic circular chart (9-hour threshold by default)
SleepCircularChartView(samples: sleepSamples)
// Custom threshold and styling
SleepCircularChartView(
samples: sleepSamples,
lineWidth: 20,
size: 200,
showIcons: false,
thresholdHours: 8.0
)
}
.padding()
}
}
thresholdHours
- Sleep duration threshold for percentage calculation (default: 9.0 hours)showIcons
- Display sun/moon icons at start/end of sleep arc (default: true)lineWidth
- Width of the circular segments (default: 16)size
- Size of the circular chart (default: 160)showLabels
- Show duration and time labels in center (default: true)
Example: If a user sleeps 7 hours with a 9-hour threshold, the circle fills 77.8% (7/9) of the way around.

import SwiftUI
import HealthKit
import SleepChartKit
@available(iOS 16.0, *)
struct HealthKitSleepView: View {
@State private var healthKitSamples: [HKCategorySample] = []
var body: some View {
// Direct HealthKit integration
SleepChartView(healthKitSamples: healthKitSamples)
.padding()
.onAppear {
loadHealthKitData()
}
}
private func loadHealthKitData() {
// Your HealthKit data loading logic
// healthKitSamples = fetchedSamples
}
}
SleepChartKit supports the following sleep stages:
- Awake - Periods of wakefulness
- REM Sleep - Rapid Eye Movement sleep
- Light Sleep - Core/light sleep stages
- Deep Sleep - Deep sleep stages
- Unspecified Sleep - General sleep periods
- In Bed - Time spent in bed (filtered when other stages present)
struct MyColorProvider: SleepStageColorProvider {
func color(for stage: SleepStage) -> Color {
switch stage {
case .awake: return .red
case .asleepREM: return .purple
case .asleepCore: return .blue
case .asleepDeep: return .indigo
case .asleepUnspecified: return .gray
case .inBed: return .secondary
}
}
}
SleepChartView(
samples: sleepSamples,
colorProvider: MyColorProvider()
)
// Using custom names
let customNameProvider = CustomSleepStageDisplayNameProvider(customNames: [
.awake: "Awake",
.asleepREM: "REM Sleep",
.asleepCore: "Light Sleep",
.asleepDeep: "Deep Sleep",
.asleepUnspecified: "Unknown Sleep",
.inBed: "In Bed"
])
SleepChartView(
samples: sleepSamples,
displayNameProvider: customNameProvider
)
// Using localized strings from your app's bundle
let localizedProvider = LocalizedSleepStageDisplayNameProvider(
bundle: .main,
tableName: "SleepStages"
)
SleepChartView(
samples: sleepSamples,
displayNameProvider: localizedProvider
)
Create a SleepStages.strings
file:
"sleep_stage_awake" = "Awake";
"sleep_stage_asleepREM" = "REM Sleep";
"sleep_stage_asleepCore" = "Light Sleep";
"sleep_stage_asleepDeep" = "Deep Sleep";
"sleep_stage_asleepUnspecified" = "Sleep";
"sleep_stage_inBed" = "In Bed";
struct MyDurationFormatter: DurationFormatter {
func format(_ duration: TimeInterval) -> String {
let hours = Int(duration) / 3600
let minutes = (Int(duration) % 3600) / 60
return "\(hours):\(String(format: "%02d", minutes))"
}
}
SleepChartView(
samples: sleepSamples,
durationFormatter: MyDurationFormatter()
)
@available(iOS 16.0, *)
SleepChartView(
healthKitSamples: healthKitSamples,
colorProvider: MyColorProvider(),
durationFormatter: MyDurationFormatter(),
displayNameProvider: LocalizedSleepStageDisplayNameProvider()
)
SleepChartKit follows SOLID principles with a clean, modular architecture:
- SleepChartView - Main timeline chart container
- SleepCircularChartView - Circular percentage-based chart with threshold support
- SleepTimelineGraph - Timeline visualization with Canvas
- SleepTimeAxisView - Time labels and axis
- SleepLegendView - Sleep stage legend
- SleepSample - Represents a sleep period
- SleepStage - Enum of sleep stages
- TimeSpan - Time axis labels
- SleepStageColorProvider - Stage color customization
- SleepStageDisplayNameProvider - Stage display name customization
- DurationFormatter - Duration text formatting
- TimeSpanGenerator - Time axis customization
SleepChartKit provides native support for HealthKit sleep analysis data with automatic conversion and type safety.
import HealthKit
import SleepChartKit
@available(iOS 16.0, *)
func createChart(with healthKitSamples: [HKCategorySample]) -> some View {
// Direct integration - automatically converts HealthKit samples
SleepChartView(healthKitSamples: healthKitSamples)
}
// Convert individual samples
let sleepSample = SleepSample(healthKitSample: hkSample)
// Batch convert samples
let sleepSamples = SleepSample.samples(from: healthKitSamples)
// Create chart with converted samples
SleepChartView(samples: sleepSamples)
// Convert between SleepStage and HKCategoryValueSleepAnalysis
let sleepStage = SleepStage(healthKitValue: .asleepREM) // Optional conversion
let healthKitValue = sleepStage.healthKitValue // Direct conversion
// Use with color providers
let color = colorProvider.color(for: .asleepREM) // HKCategoryValueSleepAnalysis
HealthKit integration requires:
- iOS 16.0+ / macOS 13.0+ / watchOS 9.0+
- HealthKit framework available
- iOS 15.0+
- macOS 12.0+
- watchOS 8.0+
- tvOS 15.0+
- Xcode 13.0+
- Swift 5.5+
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
SleepChartKit is available under the MIT license. See the LICENSE file for more info.

Sample sleep chart showing a night's sleep with deep sleep, REM, and wake periods.