Skip to main content

Apple Health Local

The RookSummaryManager class provides the following methods to fetch a user’s daily summaries. Returned data is sourced from Apple HealthKit and may include contributions from third-party apps and devices that write to HealthKit.

// Async await 
public func getSleepSummary(date: Date) async throws -> [RookSleepSummary]
public func getPhysicalSummary(date: Date) async throws -> RookPhysicalSummary
public func getBodySummary(date: Date) async throws -> RookBodySummary


// Completion
public func getSleepSummary(date: Date, completion: @escaping (Result<[RookSleepSummary], Error>) -> Void)
public func getPhysicalSummary(date: Date, completion: @escaping (Result<RookPhysicalSummary, Error>) -> Void)
public func getBodySummary(date: Date, completion: @escaping (Result<RookBodySummary, Error>) -> Void)
  • HealthKit must be available on the device and authorized for the relevant data types.

  • Data may be delayed if the user’s sources (e.g., a wearable) have not synced to Apple Health yet.

Quick Example

import SwiftUI
import RookSDK

struct SleepView: View {

let permissionManager: RookConnectPermissionsManager = RookConnectPermissionsManager()
@StateObject var viewModel: SleepViewModel = SleepViewModel()

var body: some View {
ScrollView {
if viewModel.isLoading {
ProgressView()
} else {
Text("Sleep")
.font(.system(size: 24, weight: .bold))
.padding(12)

Button(action: {
permissionManager.requestSleepPermissions() { _ in }
}, label: {
Text("get Sleep Permissions")
}).padding(20)

DatePicker("date to fetch",
selection: $viewModel.date,
displayedComponents: .date)
.pickerStyle(.wheel)
.padding(8)

Button(action: {
viewModel.syncSleepSummary()
}, label: {
Text("Get Sleep summary")
}).padding(20)
}
summaries
Spacer()
}
}

var summaries: some View {
Group {
if !viewModel.sleepSummaries.isEmpty {
LazyVStack {
ForEach(viewModel.sleepSummaries, id: \.sourceOfData) { summary in
VStack {
Text("start time: \(viewModel.dateString(summary.sleepStartDatetime))")
Text("end time: \(viewModel.dateString(summary.sleepEndDatetime))")

Text("duration: \(viewModel.timeFormat(summary.sleepDurationSeconds ?? 0))")
Text("rem time: \(viewModel.timeFormat(summary.remSleepDurationSeconds ?? 0))")
Text("deep time: \(viewModel.timeFormat(summary.deepSleepDurationSeconds ?? 0))")
Text("light time: \(viewModel.timeFormat(summary.lightSleepDurationSeconds ?? 0))")
Text("time in bed: \(viewModel.timeFormat(summary.timeInBedSeconds ?? 0))")
Text("time to fall asleep \(viewModel.timeFormat(summary.timeToFallAsleepSeconds ?? 0))")

Text("HR Max bmp: \(summary.hrMaxBPM ?? 0)")
Text("HR Min bmp: \(summary.hrMinimumBPM ?? 0)")
Text("HR Avg bmp: \(summary.hrMinimumBPM ?? 0)")

Text("Breaths per minute average : \(summary.breathsAvgPerMin ?? 0)")
Text("Breaths per minute min : \(summary.breathsMinimumPerMin ?? 0)")
Text("Breaths per minute max : \(summary.breathsMaxPerMin ?? 0)")

Text("oxygenation % average : \(summary.saturationAvgPercentage ?? 0)")
Text("oxygenation % min : \(summary.saturationMinPercentage ?? 0)")
Text("oxygenation % max : \(summary.saturationMaxPercentage ?? 0)")

Text("Temperature wrist max : \(summary.maxWristTemperature ?? .zero)")
Text("Temperature wrist min : \(summary.minWristTemperature ?? .zero)")
Text("Temperature wrist average : \(summary.averageWristTemperature ?? .zero)")


getSleepSamplesView(summary)
}
}
}
} else {
EmptyView()
}
}
}

@ViewBuilder
func getSleepSamplesView(_ summary: RookSleepSummary) -> some View {
if let samples: [RookSleepZoneSample] = summary.sleepSamples {
let zones: [String : [RookSleepZoneSample]] = viewModel.getZones(samples)
VStack {
ForEach(zones.keys.sorted(), id: \.self) { zone in
HStack {
Text("\(zones[zone]?.count ?? 0), \(zone): \(viewModel.getZoneDuration(zones[zone] ?? []))")
}
}
}
} else {
EmptyView()
}
}
}
import Foundation
import RookSDK

class SleepViewModel: ObservableObject {

private let summaryManager: RookSummaryManager = RookSummaryManager()

@Published var isLoading: Bool = false
@Published var date: Date = Date()
@Published var sleepSummaries: [RookSleepSummary] = []

func syncSleepSummary() {
self.isLoading = true
Task {
do {
let summaries: [RookSleepSummary] = try await summaryManager.getSleepSummary(date: date)
DispatchQueue.main.async {
self.sleepSummaries = summaries
}
} catch {
debugPrint(("error \(error.localizedDescription)"))
}
DispatchQueue.main.async {
self.isLoading = false
}
}
}

func timeFormat(_ seconds: Int) -> String {
let hours = seconds / 3600
let minutes = (seconds % 3600) / 60
let secs = seconds % 60
return String(format: "%02d:%02d:%02d", hours, minutes, secs)
}

func dateString(_ date: Date) -> String {
let dateFormatter: DateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter.string(from: date)
}