Add view of most samples

This commit is contained in:
Christoph Hagen
2024-04-15 15:55:10 +02:00
parent 778e83682a
commit 03469446e1
29 changed files with 900 additions and 60 deletions

View File

@@ -32,7 +32,6 @@ struct HealthImportApp: App {
DispatchQueue.main.async {
// Go back to main queue so that list will be updated
guard let databaseToLoad = databaseList.databases.first(where: { $0.isDefault }) else {
print("No default database to load")
return
}
Task {
@@ -41,7 +40,6 @@ struct HealthImportApp: App {
return
}
DispatchQueue.main.async {
print("Setting selection to workouts")
self.selection = .workouts
}
}

View File

@@ -3,20 +3,32 @@ import SQLite
import HealthKit
import HealthDB
extension Database {
extension HealthDatabase {
private static let databaseFileUrl = Bundle.main.url(forResource: "healthdb_secure", withExtension: "sqlite")
static var mock: Database {
let bundleUrl = Database.databaseFileUrl!
static var mock: HealthDatabase {
let bundleUrl = databaseFileUrl!
let local = FileManager.default.documentDirectory.appendingPathComponent("db.sqlite")
if !FileManager.default.fileExists(atPath: local.path) {
try! FileManager.default.copyItem(at: bundleUrl, to: local)
}
let store = try! HealthDatabase(fileUrl: local)
return .init(store: store)
return try! HealthDatabase(fileUrl: local)
}
static var empty: HealthDatabase {
let store = try! HKDatabaseStore(database: Connection(.inMemory))
try! store.createTables()
return .init(wrapping: store)
}
}
extension Database {
// static var mock: Database {
// return .init(store: .mock)
// }
static var empty: Database {
do {

View File

@@ -10,9 +10,9 @@ extension HKWorkoutEvent {
duration: 1114.56374406815),
metadata: [
"_HKPrivateMetadataTotalDistanceQuantity": HKQuantity(unit: .meter(), doubleValue: 1000),
"_HKPrivateWorkoutSegmentEventSubtype": NSNumber(1),
"_HKPrivateWorkoutSegmentEventSubtype": NSNumber(value: UInt64(1)),
"_HKPrivateMetadataSplitDistanceQuantity": HKQuantity(unit: .meter(), doubleValue: 1000),
"_HKPrivateMetadataSplitMeasuringSystem": NSNumber(1),
"_HKPrivateMetadataSplitMeasuringSystem": NSNumber(value: UInt64(1)),
"_HKPrivateMetadataIsPartialSplit": NSNumber(0),
"_HKPrivateMetadataSplitActiveDurationQuantity": HKQuantity(unit: .second(), doubleValue: 1114.56)
]),
@@ -22,10 +22,10 @@ extension HKWorkoutEvent {
metadata: [
"_HKPrivateMetadataSplitDistanceQuantity": HKQuantity(unit: .meter(), doubleValue: 1609.34),
"_HKPrivateMetadataSplitActiveDurationQuantity": HKQuantity(unit: .second(), doubleValue: 1972.17),
"_HKPrivateMetadataIsPartialSplit": 0,
"_HKPrivateMetadataIsPartialSplit": NSNumber(value: UInt64(0)),
"_HKPrivateMetadataTotalDistanceQuantity": HKQuantity(unit: .meter(), doubleValue: 1609.34),
"_HKPrivateWorkoutSegmentEventSubtype": 1,
"_HKPrivateMetadataSplitMeasuringSystem": 2
"_HKPrivateWorkoutSegmentEventSubtype": NSNumber(value: UInt64(1)),
"_HKPrivateMetadataSplitMeasuringSystem": NSNumber(value: UInt64(2))
]),
.init(type: .init(rawValue: 1)!,
dateInterval: .init(start: Date(timeIntervalSinceReferenceDate: 702112942.707113),
@@ -33,10 +33,10 @@ extension HKWorkoutEvent {
metadata: [
"_HKPrivateMetadataSplitDistanceQuantity": HKQuantity(unit: .meter(), doubleValue: 1609.34),
"_HKPrivateMetadataSplitActiveDurationQuantity": HKQuantity(unit: .second(), doubleValue: 1972.17),
"_HKPrivateMetadataIsPartialSplit": 0,
"_HKPrivateMetadataIsPartialSplit": NSNumber(value: UInt64(0)),
"_HKPrivateMetadataTotalDistanceQuantity": HKQuantity(unit: .meter(), doubleValue: 1609.34),
"_HKPrivateWorkoutSegmentEventSubtype": 1,
"_HKPrivateMetadataSplitMeasuringSystem": 2
"_HKPrivateWorkoutSegmentEventSubtype": NSNumber(value: UInt64(1)),
"_HKPrivateMetadataSplitMeasuringSystem": NSNumber(value: UInt64(2)),
]),
.init(type: .init(rawValue: 2)!,
dateInterval: .init(start: Date(timeIntervalSinceReferenceDate: 702113161.221132),

View File

@@ -1,15 +0,0 @@
import SwiftUI
struct ActivitySampleList: View {
var body: some View {
List {
}
.navigationTitle("Activity")
}
}
#Preview {
ActivitySampleList()
}

View File

@@ -0,0 +1,27 @@
import SwiftUI
import HealthDB
import HealthKit
import HealthKitExtensions
struct CategoryEmptySampleList<T>: View where T: HKCategoryEmptySample {
let database: HealthDatabase
@State
private var samples: [T] = []
var body: some View {
GenericSampleList(sampleView: { sample in
Text(sample.endDate.formatted())
}, search: { start, end, _ -> [T] in
try database.samples(from: start, to: end)
})
.navigationTitle(T.categoryTypeIdentifier.description)
}
}
#Preview {
NavigationStack {
CategoryEmptySampleList<MindfulSession>(database: .empty)
}
}

View File

@@ -0,0 +1,27 @@
import SwiftUI
import HealthKitExtensions
import HealthDB
struct CategoryEmptySampleRow<T>: View where T: HKCategoryEmptySample {
let database: HealthDatabase
let title: String?
init(database: HealthDatabase, title: String? = nil) {
self.database = database
self.title = title
}
var body: some View {
NavigationLink {
CategoryEmptySampleList<T>(database: database)
} label: {
Text(title ?? T.categoryTypeIdentifier.description)
}
}
}
#Preview {
CategoryEmptySampleRow<MindfulSession>(database: .empty)
}

View File

@@ -0,0 +1,31 @@
import SwiftUI
import HealthDB
import HealthKit
import HealthKitExtensions
struct CategoryEnumSampleList<T>: View where T: HKCategoryEnumSample {
let database: HealthDatabase
@State
private var samples: [T] = []
var body: some View {
GenericSampleList(sampleView: { sample in
HStack {
Text("\(sample.value)")
Spacer()
Text(sample.endDate.formatted())
}
}, search: { start, end, _ -> [T] in
try database.samples(from: start, to: end)
})
.navigationTitle(T.categoryTypeIdentifier.description)
}
}
#Preview {
NavigationStack {
CategoryEnumSampleList<AbdominalCramps>(database: .empty)
}
}

View File

@@ -0,0 +1,27 @@
import SwiftUI
import HealthKitExtensions
import HealthDB
struct CategoryEnumSampleRow<T>: View where T: HKCategoryEnumSample {
let database: HealthDatabase
let title: String?
init(database: HealthDatabase, title: String? = nil) {
self.database = database
self.title = title
}
var body: some View {
NavigationLink {
CategoryEnumSampleList<T>(database: database)
} label: {
Text(title ?? T.categoryTypeIdentifier.description)
}
}
}
#Preview {
CategoryEnumSampleRow<AbdominalCramps>(database: .empty)
}

View File

@@ -0,0 +1,82 @@
import SwiftUI
import HealthDB
import HealthKit
import HealthKitExtensions
struct GenericSampleList<Sample, Content>: View where Content: View, Sample: HKSampleContainer {
let sampleView: (Sample) -> Content
let search: (Date, Date, Bool) throws -> [Sample]
@State
private var samples: [Sample] = []
@State
private var startDate = Date.now.addingTimeInterval(-86400)
@State
private var endDate = Date.now
@State
private var sortAscending = false
var body: some View {
List {
Section("Search") {
DatePicker("Start", selection: $startDate)
.datePickerStyle(.compact)
DatePicker("End", selection: $endDate)
.datePickerStyle(.compact)
Toggle("Sort ascending", isOn: $sortAscending)
Button(action: load) {
HStack {
Spacer()
Text("Find samples")
Spacer()
}
}
}
Section("Samples") {
ForEach(samples, id: \.uuid) { sample in
sampleView(sample)
}
}
}.onAppear {
load()
}
}
func load() {
print("Finding samples from \(startDate.formatted()) to \(endDate.formatted()) for \(Sample.self)")
Task {
do {
try await loadSamplesAsync()
} catch {
print("Failed to load samples: \(error)")
}
}
}
private func loadSamplesAsync() async throws {
let samples = try search(startDate, endDate, sortAscending)
.sorted(ascending: false) { $0.endDate }
print("Finished loading \(samples.count) samples")
DispatchQueue.main.async {
self.samples = samples
}
}
}
#Preview {
NavigationStack {
GenericSampleList(sampleView: { sample in
HStack {
Text(sample.quantity.description)
Spacer()
Text(sample.endDate.formatted())
}
}, search: { (_, _, _) -> [ActiveEnergyBurned] in
[] })
}
}

View File

@@ -0,0 +1,44 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct ActivitySamplesList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<ActiveEnergyBurned>(database: database)
QuantitySampleRow<AppleExerciseTime>(database: database)
QuantitySampleRow<AppleMoveTime>(database: database)
QuantitySampleRow<AppleStandTime>(database: database)
QuantitySampleRow<BasalEnergyBurned>(database: database)
QuantitySampleRow<CyclingCadence>(database: database)
QuantitySampleRow<CyclingFunctionalThresholdPower>(database: database)
QuantitySampleRow<CyclingPower>(database: database)
QuantitySampleRow<CyclingSpeed>(database: database)
QuantitySampleRow<DistanceCycling>(database: database)
QuantitySampleRow<DistanceDownhillSnowSports>(database: database)
QuantitySampleRow<FlightsClimbed>(database: database)
QuantitySampleRow<NikeFuel>(database: database)
QuantitySampleRow<PhysicalEffort>(database: database)
QuantitySampleRow<PushCount>(database: database)
QuantitySampleRow<RunningPower>(database: database)
QuantitySampleRow<RunningSpeed>(database: database)
QuantitySampleRow<StepCount>(database: database)
QuantitySampleRow<DistanceSwimming>(database: database)
QuantitySampleRow<SwimmingStrokeCount>(database: database)
QuantitySampleRow<UnderwaterDepth>(database: database)
QuantitySampleRow<DistanceWalkingRunning>(database: database)
QuantitySampleRow<DistanceWheelchair>(database: database)
}
.navigationTitle("Activity")
}
}
#Preview {
NavigationStack {
ActivitySamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,32 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct BodyMeasurementsList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<BasalBodyTemperature>(database: database)
QuantitySampleRow<BodyFatPercentage>(database: database)
QuantitySampleRow<BodyMass>(database: database)
QuantitySampleRow<BodyMassIndex>(database: database)
QuantitySampleRow<BodyTemperature>(database: database)
QuantitySampleRow<ElectrodermalActivity>(database: database)
QuantitySampleRow<Height>(database: database)
QuantitySampleRow<LeanBodyMass>(database: database)
Text("Vision prescription")
.foregroundStyle(.secondary)
QuantitySampleRow<WaistCircumference>(database: database)
QuantitySampleRow<AppleSleepingWristTemperature>(database: database, title: "Wrist temperature")
}
.navigationTitle("Body Measurements")
}
}
#Preview {
NavigationStack {
BodyMeasurementsList(database: .empty)
}
}

View File

@@ -0,0 +1,57 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct CycleTrackingList: View {
let database: HealthDatabase
var body: some View {
List {
CategoryEnumSampleRow<AbdominalCramps>(database: database)
CategoryEnumSampleRow<Acne>(database: database)
CategoryEnumSampleRow<AppetiteChanges>(database: database)
QuantitySampleRow<BasalBodyTemperature>(database: database)
CategoryEnumSampleRow<BladderIncontinence>(database: database)
CategoryEnumSampleRow<Bloating>(database: database)
CategoryEnumSampleRow<BreastPain>(database: database)
CategoryEnumSampleRow<CervicalMucusQuality>(database: database)
CategoryEnumSampleRow<Chills>(database: database)
CategoryEnumSampleRow<Constipation>(database: database)
CategoryEnumSampleRow<Contraceptive>(database: database)
CategoryEnumSampleRow<Diarrhea>(database: database)
CategoryEnumSampleRow<DrySkin>(database: database)
CategoryEnumSampleRow<Fatigue>(database: database)
CategoryEnumSampleRow<HairLoss>(database: database)
CategoryEnumSampleRow<Headache>(database: database)
CategoryEnumSampleRow<HotFlashes>(database: database)
CategoryEmptySampleRow<InfrequentMenstrualCycles>(database: database)
CategoryEmptySampleRow<IntermenstrualBleeding>(database: database)
CategoryEmptySampleRow<IrregularMenstrualCycles>(database: database)
CategoryEmptySampleRow<Lactation>(database: database)
CategoryEnumSampleRow<LowerBackPain>(database: database)
CategoryEnumSampleRow<MemoryLapse>(database: database)
CategoryEnumSampleRow<MenstrualFlow>(database: database)
CategoryEnumSampleRow<MoodChanges>(database: database)
CategoryEnumSampleRow<Nausea>(database: database)
CategoryEnumSampleRow<NightSweats>(database: database)
CategoryEnumSampleRow<OvulationTestResult>(database: database)
CategoryEnumSampleRow<PelvicPain>(database: database)
CategoryEmptySampleRow<PersistentIntermenstrualBleeding>(database: database)
CategoryEmptySampleRow<Pregnancy>(database: database)
CategoryEnumSampleRow<PregnancyTestResult>(database: database)
CategoryEnumSampleRow<ProgesteroneTestResult>(database: database)
CategoryEmptySampleRow<ProlongedMenstrualPeriods>(database: database)
CategoryEmptySampleRow<SexualActivity>(database: database)
CategoryEnumSampleRow<SleepChanges>(database: database)
CategoryEnumSampleRow<VaginalDryness>(database: database)
}
.navigationTitle("Cycle Tracking")
}
}
#Preview {
NavigationStack {
CycleTrackingList(database: .empty)
}
}

View File

@@ -0,0 +1,27 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct HearingSamplesList: View {
let database: HealthDatabase
var body: some View {
List {
Text("Audiogram")
.foregroundStyle(.secondary)
QuantitySampleRow<EnvironmentalAudioExposure>(database: database)
CategoryEnumSampleRow<EnvironmentalAudioExposureEvent>(database: database)
QuantitySampleRow<EnvironmentalSoundReduction>(database: database)
QuantitySampleRow<HeadphoneAudioExposure>(database: database)
CategoryEnumSampleRow<HeadphoneAudioExposureEvent>(database: database)
}
.navigationTitle("Hearing")
}
}
#Preview {
NavigationStack {
HearingSamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,39 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct HeartSamplesList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<HeartRate>(database: database)
QuantitySampleRow<HeartRateVariabilitySDNN>(database: database, title: "Heart Rate Variability")
QuantitySampleRow<RestingHeartRate>(database: database)
QuantitySampleRow<WalkingHeartRateAverage>(database: database)
QuantitySampleRow<Vo2Max>(database: database, title: "Cardio Fitness")
CategoryEmptySampleRow<LowHeartRateEvent>(database: database)
Text("Electrocardiograms (ECG)")
.foregroundStyle(.secondary)
#warning("Create view for Electrocardiograms")
QuantitySampleRow<AtrialFibrillationBurden>(database: database)
Text("Blood Pressure")
.foregroundStyle(.secondary)
#warning("Create view for blood pressure")
QuantitySampleRow<BloodPressureDiastolic>(database: database)
QuantitySampleRow<BloodPressureSystolic>(database: database)
CategoryEnumSampleRow<LowCardioFitnessEvent>(database: database)
CategoryEmptySampleRow<HighHeartRateEvent>(database: database)
CategoryEmptySampleRow<IrregularHeartRhythmEvent>(database: database)
QuantitySampleRow<PeripheralPerfusionIndex>(database: database)
}
.navigationTitle("Heart")
}
}
#Preview {
NavigationStack {
HeartSamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,31 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct MentalHealthList: View {
let database: HealthDatabase
var body: some View {
List {
Text("Anxiety Risk")
.foregroundStyle(.secondary)
Text("Depression Risk")
.foregroundStyle(.secondary)
QuantitySampleRow<AppleExerciseTime>(database: database, title: "Exercise Minutes")
CategoryEmptySampleRow<MindfulSession>(database: database, title: "Mindful Minutes")
Text("Sleep")
.foregroundStyle(.secondary)
Text("State of Mind")
.foregroundStyle(.secondary)
QuantitySampleRow<TimeInDaylight>(database: database)
}
.navigationTitle("Mental Wellbeing")
}
}
#Preview {
NavigationStack {
MentalHealthList(database: .empty)
}
}

View File

@@ -0,0 +1,33 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct MobilitySamplesList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<Vo2Max>(database: database, title: "Cardio Fitness")
QuantitySampleRow<WalkingDoubleSupportPercentage>(database: database, title: "Double Support Time")
QuantitySampleRow<RunningGroundContactTime>(database: database, title: "Ground Contact Time")
QuantitySampleRow<RunningStrideLength>(database: database)
QuantitySampleRow<SixMinuteWalkTestDistance>(database: database, title: "Six-Minute Walk")
QuantitySampleRow<StairDescentSpeed>(database: database, title: "Stair Speed: Down")
QuantitySampleRow<StairAscentSpeed>(database: database, title: "Stair Speed: Up")
QuantitySampleRow<RunningVerticalOscillation>(database: database, title: "Vertical Oscillation")
QuantitySampleRow<WalkingAsymmetryPercentage>(database: database, title: "Walking Asymmetry")
QuantitySampleRow<WalkingSpeed>(database: database)
QuantitySampleRow<AppleWalkingSteadiness>(database: database, title: "Walking Steadiness")
QuantitySampleRow<WalkingStepLength>(database: database)
CategoryEnumSampleRow<AppleWalkingSteadinessEvent>(database: database, title: "Walking Steadiness Notifications")
}
.navigationTitle("Mobility")
}
}
#Preview {
NavigationStack {
MobilitySamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,59 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct NutritionSamplesList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<DietaryBiotin>(database: database, title: "Biotin")
QuantitySampleRow<DietaryCaffeine>(database: database, title: "Caffeine")
QuantitySampleRow<DietaryCalcium>(database: database, title: "Calcium")
QuantitySampleRow<DietaryCarbohydrates>(database: database, title: "Carbohydrates")
QuantitySampleRow<DietaryChloride>(database: database, title: "Chloride")
QuantitySampleRow<DietaryChromium>(database: database, title: "Chromium")
QuantitySampleRow<DietaryCopper>(database: database, title: "Copper")
QuantitySampleRow<DietaryCholesterol>(database: database)
QuantitySampleRow<DietaryEnergyConsumed>(database: database, title: "Dietary Energy")
QuantitySampleRow<DietarySugar>(database: database)
QuantitySampleRow<DietaryFiber>(database: database, title: "Fiber")
QuantitySampleRow<DietaryFolate>(database: database, title: "Folate")
QuantitySampleRow<DietaryIodine>(database: database, title: "Iodine")
QuantitySampleRow<DietaryIron>(database: database, title: "Iron")
QuantitySampleRow<DietaryMagnesium>(database: database, title: "Magnesium")
QuantitySampleRow<DietaryManganese>(database: database, title: "Manganese")
QuantitySampleRow<DietaryMolybdenum>(database: database, title: "Molybdenum")
QuantitySampleRow<DietaryFatMonounsaturated>(database: database, title: "Monounsaturated Fat")
QuantitySampleRow<DietaryNiacin>(database: database, title: "Niacin")
QuantitySampleRow<DietaryPantothenicAcid>(database: database, title: "Pantothenic Acid")
QuantitySampleRow<DietaryPhosphorus>(database: database, title: "Phosphorus")
QuantitySampleRow<DietaryFatPolyunsaturated>(database: database, title: "Polyunsaturated Fat")
QuantitySampleRow<DietaryPotassium>(database: database, title: "Potassium")
QuantitySampleRow<DietaryProtein>(database: database, title: "Protein")
QuantitySampleRow<DietaryRiboflavin>(database: database, title: "Riboflavin")
QuantitySampleRow<DietaryFatSaturated>(database: database, title: "Saturated Fat")
QuantitySampleRow<DietarySelenium>(database: database, title: "Selenium")
QuantitySampleRow<DietarySodium>(database: database, title: "Sodium")
QuantitySampleRow<DietaryThiamin>(database: database, title: "Thiamine")
QuantitySampleRow<DietaryFatTotal>(database: database, title: "Total Fat")
QuantitySampleRow<DietaryVitaminA>(database: database, title: "Vitamin A")
QuantitySampleRow<DietaryVitaminB6>(database: database, title: "Vitamin B6")
QuantitySampleRow<DietaryVitaminB12>(database: database, title: "Vitamin B12")
QuantitySampleRow<DietaryVitaminC>(database: database, title: "Vitamin C")
QuantitySampleRow<DietaryVitaminD>(database: database, title: "Vitamin D")
QuantitySampleRow<DietaryVitaminE>(database: database, title: "Vitamin E")
QuantitySampleRow<DietaryVitaminK>(database: database, title: "Vitamin K")
QuantitySampleRow<DietaryWater>(database: database, title: "Water")
QuantitySampleRow<DietaryZinc>(database: database, title: "Zinc")
}
.navigationTitle("Nutrition")
}
}
#Preview {
NavigationStack {
NutritionSamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,31 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct OtherSamplesList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<BloodAlcoholContent>(database: database)
QuantitySampleRow<BloodGlucose>(database: database)
CategoryEmptySampleRow<HandwashingEvent>(database: database)
QuantitySampleRow<InhalerUsage>(database: database)
QuantitySampleRow<InsulinDelivery>(database: database)
QuantitySampleRow<NumberOfTimesFallen>(database: database, title: "Number of Times Fallen")
CategoryEmptySampleRow<SexualActivity>(database: database)
QuantitySampleRow<TimeInDaylight>(database: database)
CategoryEmptySampleRow<ToothbrushingEvent>(database: database)
QuantitySampleRow<UvExposure>(database: database, title: "UV Index")
QuantitySampleRow<WaterTemperature>(database: database)
}
.navigationTitle("Other Data")
}
}
#Preview {
NavigationStack {
OtherSamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,28 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct RespiratorySamplesList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<OxygenSaturation>(database: database, title: "Blood Oxygen")
QuantitySampleRow<Vo2Max>(database: database, title: "Cardio Fitness")
QuantitySampleRow<ForcedExpiratoryVolume1>(database: database, title: "Forced Expiratory Volume, 1 sec")
QuantitySampleRow<ForcedVitalCapacity>(database: database)
QuantitySampleRow<InhalerUsage>(database: database)
QuantitySampleRow<PeakExpiratoryFlowRate>(database: database)
QuantitySampleRow<RespiratoryRate>(database: database)
QuantitySampleRow<SixMinuteWalkTestDistance>(database: database, title: "Six Minute Walk Distance")
}
.navigationTitle("Respiratory")
}
}
#Preview {
NavigationStack {
RespiratorySamplesList(database: .empty)
}
}

View File

@@ -0,0 +1,57 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct SymptomsList: View {
let database: HealthDatabase
var body: some View {
List {
CategoryEnumSampleRow<AbdominalCramps>(database: database)
CategoryEnumSampleRow<Acne>(database: database)
CategoryEnumSampleRow<AppetiteChanges>(database: database)
CategoryEnumSampleRow<BladderIncontinence>(database: database)
CategoryEnumSampleRow<Bloating>(database: database)
CategoryEnumSampleRow<GeneralizedBodyAche>(database: database, title: "Body and Muscle Ache")
CategoryEnumSampleRow<BreastPain>(database: database)
CategoryEnumSampleRow<ChestTightnessOrPain>(database: database)
CategoryEnumSampleRow<Chills>(database: database)
CategoryEnumSampleRow<SinusCongestion>(database: database, title: "Congestion")
CategoryEnumSampleRow<Constipation>(database: database)
CategoryEnumSampleRow<Coughing>(database: database)
CategoryEnumSampleRow<Diarrhea>(database: database, title: "Diarrhoea")
CategoryEnumSampleRow<DrySkin>(database: database)
CategoryEnumSampleRow<Fainting>(database: database)
CategoryEnumSampleRow<Fatigue>(database: database)
CategoryEnumSampleRow<Fever>(database: database)
CategoryEnumSampleRow<HairLoss>(database: database)
CategoryEnumSampleRow<Headache>(database: database)
CategoryEnumSampleRow<Heartburn>(database: database)
CategoryEnumSampleRow<HotFlashes>(database: database)
CategoryEnumSampleRow<LossOfSmell>(database: database)
CategoryEnumSampleRow<LossOfTaste>(database: database)
CategoryEnumSampleRow<LowerBackPain>(database: database)
CategoryEnumSampleRow<MemoryLapse>(database: database)
CategoryEnumSampleRow<MoodChanges>(database: database)
CategoryEnumSampleRow<Nausea>(database: database)
CategoryEnumSampleRow<NightSweats>(database: database)
CategoryEnumSampleRow<PelvicPain>(database: database)
CategoryEnumSampleRow<RapidPoundingOrFlutteringHeartbeat>(database: database)
CategoryEnumSampleRow<RunnyNose>(database: database)
CategoryEnumSampleRow<ShortnessOfBreath>(database: database)
CategoryEnumSampleRow<SleepChanges>(database: database)
CategoryEnumSampleRow<SoreThroat>(database: database)
CategoryEnumSampleRow<VaginalDryness>(database: database)
CategoryEnumSampleRow<Vomiting>(database: database)
CategoryEnumSampleRow<Wheezing>(database: database)
}
.navigationTitle("Symptoms")
}
}
#Preview {
NavigationStack {
SymptomsList(database: .empty)
}
}

View File

@@ -0,0 +1,29 @@
import SwiftUI
import HealthDB
import HealthKitExtensions
struct VitalsList: View {
let database: HealthDatabase
var body: some View {
List {
QuantitySampleRow<BloodGlucose>(database: database)
QuantitySampleRow<OxygenSaturation>(database: database, title: "Blood Oxygen")
Text("Blood Pressure")
.foregroundStyle(.secondary)
QuantitySampleRow<BodyTemperature>(database: database)
QuantitySampleRow<HeartRate>(database: database)
CategoryEnumSampleRow<MenstrualFlow>(database: database)
QuantitySampleRow<RespiratoryRate>(database: database)
}
.navigationTitle("Vitals")
}
}
#Preview {
NavigationStack {
VitalsList(database: .empty)
}
}

View File

@@ -0,0 +1,28 @@
import SwiftUI
import HealthDB
import HealthKit
import HealthKitExtensions
struct QuantitySampleList<T>: View where T: HKQuantitySampleContainer {
let database: HealthDatabase
var body: some View {
GenericSampleList(sampleView: { sample in
HStack {
Text(sample.quantity.description)
Spacer()
Text(sample.endDate.formatted())
}
}, search: { start, end, _ -> [T] in
try database.samples(includingSeriesData: true, from: start, to: end)
})
.navigationTitle(T.quantityTypeIdentifier.description)
}
}
#Preview {
NavigationStack {
QuantitySampleList<ActiveEnergyBurned>(database: .empty)
}
}

View File

@@ -0,0 +1,27 @@
import SwiftUI
import HealthKitExtensions
import HealthDB
struct QuantitySampleRow<T>: View where T: HKQuantitySampleContainer {
let database: HealthDatabase
let title: String?
init(database: HealthDatabase, title: String? = nil) {
self.database = database
self.title = title
}
var body: some View {
NavigationLink {
QuantitySampleList<T>(database: database)
} label: {
Text(title ?? T.quantityTypeIdentifier.description)
}
}
}
#Preview {
QuantitySampleRow<ActiveEnergyBurned>(database: .empty)
}

View File

@@ -8,26 +8,73 @@ struct SamplesTab: View {
var body: some View {
NavigationStack {
List {
NavigationLink {
ActivitySampleList()
} label: {
Label("Activity", systemSymbol: .flame)
if let db = database.store {
NavigationLink {
ActivitySamplesList(database: db)
} label: {
Label("Activity", systemSymbol: .flame)
}
NavigationLink {
BodyMeasurementsList(database: db)
} label: {
Label("Body Measurements", systemSymbol: .figure)
}
Label("Clinical documents", systemSymbol: .listClipboard)
.foregroundColor(.secondary)
NavigationLink {
CycleTrackingList(database: db)
} label: {
Label("Cycle Tracking", systemSymbol: .circleHexagonpath)
}
NavigationLink {
HearingSamplesList(database: db)
} label: {
Label("Hearing", systemSymbol: .ear)
}
NavigationLink {
HeartSamplesList(database: db)
} label: {
Label("Heart", systemSymbol: .heart)
}
Label("Medications", systemSymbol: .pills)
.foregroundColor(.secondary)
NavigationLink {
MentalHealthList(database: db)
} label: {
Label("Mental Wellbeing", systemSymbol: .brainHeadProfile)
}
NavigationLink {
MobilitySamplesList(database: db)
} label: {
Label("Mobility", systemSymbol: .arrowLeftAndRight)
}
NavigationLink {
NutritionSamplesList(database: db)
} label: {
Label("Nutrition", systemSymbol: .carrot)
}
NavigationLink {
RespiratorySamplesList(database: db)
} label: {
Label("Respiratory", systemSymbol: .lungs)
}
Label("Sleep", systemSymbol: .bedDouble)
.foregroundColor(.secondary)
NavigationLink {
SymptomsList(database: db)
} label: {
Label("Symptoms", systemSymbol: .listBulletClipboard)
}
NavigationLink {
VitalsList(database: db)
} label: {
Label("Vitals", systemSymbol: .waveformPathEcgRectangle)
}
Label("Other Data", systemSymbol: .cross)
} else {
Text("No database loaded")
}
Label("Body Measurements", systemSymbol: .figure)
Label("Cycle Tracking", systemSymbol: .circleHexagonpath)
Label("Hearing", systemSymbol: .ear)
Label("Heart", systemSymbol: .heart)
Label("Medications", systemSymbol: .pills)
Label("Mental Wellbeing", systemSymbol: .brainHeadProfile)
Label("Mobility", systemSymbol: .arrowLeftAndRight)
Label("Nutrition", systemSymbol: .carrot)
Label("Respiratory", systemSymbol: .lungs)
Label("Sleep", systemSymbol: .bedDouble)
Label("Symptoms", systemSymbol: .listBulletClipboard)
Label("Vitals", systemSymbol: .waveformPathEcgRectangle)
Label("Other Data", systemSymbol: .cross)
}
.navigationTitle("Health")
}
@@ -35,6 +82,6 @@ struct SamplesTab: View {
}
#Preview {
SamplesTab(database: Database())
SamplesTab(database: Database.empty)
.preferredColorScheme(.dark)
}

View File

@@ -146,5 +146,5 @@ struct WorkoutTab: View {
#Preview {
WorkoutTab()
.environmentObject(Database.mock)
.environmentObject(Database.empty)
}

View File

@@ -120,6 +120,6 @@ struct ActivityDetailView: View {
#Preview {
NavigationStack {
ActivityDetailView(workout: .mock1, activity: .mock1)
.environmentObject(Database.mock)
.environmentObject(Database.empty)
}
}

View File

@@ -444,7 +444,7 @@ struct WorkoutDetailView: View {
#Preview {
return NavigationStack {
WorkoutDetailView(workout: .mock1)
.environmentObject(Database.mock)
.environmentObject(Database.empty)
.preferredColorScheme(.dark)
}
}