Skip to content

Commit bf78740

Browse files
LabManager + Fever Monitoring + Alerts (#30)
# Fever Monitoring and Timely Febrile Neutropenia Alerts + Customized Onboarding - LabResultsManager was implemented and used in fever monitoring. In Debug View with a button, fever monitoring logic was tested. - Fever monitoring and timely alerts for febrile neutropenia have been implemented. Notifications can run in the background or foreground. - Customized onboarding, contacts, and app icon for our app. - Added unit tests for fever monitoring. ## 📝 Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md): - [X] I agree to follow the [Code of Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md). --------- Co-authored-by: Sixian Du <[email protected]>
1 parent cc543ce commit bf78740

22 files changed

+507
-58
lines changed

NeutroFeverGuard.xcodeproj/project.pbxproj

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,16 @@
6565
A9D83F962B083794000D0C78 /* SpeziFirebaseAccountStorage in Frameworks */ = {isa = PBXBuildFile; productRef = A9D83F952B083794000D0C78 /* SpeziFirebaseAccountStorage */; };
6666
A9DFE8A92ABE551400428242 /* AccountButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DFE8A82ABE551400428242 /* AccountButton.swift */; };
6767
A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */; };
68+
B03D0E432D7D22700024F2CA /* HealthKitService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2E6898E2D52ED8500869C4F /* HealthKitService.swift */; };
69+
B03D0E462D7D24030024F2CA /* DataType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EC8332D501C5C005C3495 /* DataType.swift */; };
70+
B03D0E482D7D24AA0024F2CA /* DataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22393792D5D4092006C8EB4 /* DataError.swift */; };
71+
B08E39F92D7C416000077929 /* FeverMonitoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C95E382D78D5F2007A4CA6 /* FeverMonitoring.swift */; };
72+
B0C95E3A2D78D5F3007A4CA6 /* FeverMonitoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C95E382D78D5F2007A4CA6 /* FeverMonitoring.swift */; };
6873
B0D4C9382D790F65000643C7 /* LabResultsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D4C9372D790F5E000643C7 /* LabResultsManager.swift */; };
74+
B0DF8DF72D7BFEFB00581A00 /* FeverMonitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0DF8DF62D7BFEF600581A00 /* FeverMonitorTests.swift */; };
75+
B0DF8DFC2D7C117800581A00 /* HealthDataFetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0DF8DFB2D7C114900581A00 /* HealthDataFetchable.swift */; };
76+
B0DF8DFD2D7C123200581A00 /* HealthDataFetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0DF8DFB2D7C114900581A00 /* HealthDataFetchable.swift */; };
77+
B0FF4DD22D797FEA0076A043 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FF4DD12D797FEA0076A043 /* NotificationManager.swift */; };
6978
C42B98FF2D769E54005D820B /* SpeziKeychainStorage in Frameworks */ = {isa = PBXBuildFile; productRef = C42B98FE2D769E54005D820B /* SpeziKeychainStorage */; };
7079
C42C1B312D782A7600EA417F /* MedicationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C1B302D782A7100EA417F /* MedicationView.swift */; };
7180
C42C1C6B2D7A985B00EA417F /* MedicationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C1C6A2D7A985B00EA417F /* MedicationManager.swift */; };
@@ -113,7 +122,6 @@
113122
2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissions.swift; sourceTree = "<group>"; };
114123
2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
115124
2FAEC07F297F583900C11C42 /* NeutroFeverGuard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NeutroFeverGuard.entitlements; sourceTree = "<group>"; };
116-
2FC94CD4298B0A1D009C8209 /* NeutroFeverGuard.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = NeutroFeverGuard.xctestplan; sourceTree = "<group>"; };
117125
2FC975A72978F11A00BA99FE /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
118126
2FE5DC2529EDD38A004B9AB4 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
119127
2FE5DC2A29EDD78D004B9AB4 /* AppIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AppIcon.png; sourceTree = "<group>"; };
@@ -143,7 +151,11 @@
143151
A9A3DCC72C75CB9A00FC9B69 /* FirebaseConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseConfiguration.swift; sourceTree = "<group>"; };
144152
A9DFE8A82ABE551400428242 /* AccountButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountButton.swift; sourceTree = "<group>"; };
145153
A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSheet.swift; sourceTree = "<group>"; };
154+
B0C95E382D78D5F2007A4CA6 /* FeverMonitoring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeverMonitoring.swift; sourceTree = "<group>"; };
146155
B0D4C9372D790F5E000643C7 /* LabResultsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabResultsManager.swift; sourceTree = "<group>"; };
156+
B0DF8DF62D7BFEF600581A00 /* FeverMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeverMonitorTests.swift; sourceTree = "<group>"; };
157+
B0DF8DFB2D7C114900581A00 /* HealthDataFetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthDataFetchable.swift; sourceTree = "<group>"; };
158+
B0FF4DD12D797FEA0076A043 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
147159
C42C1B302D782A7100EA417F /* MedicationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MedicationView.swift; sourceTree = "<group>"; };
148160
C42C1C6A2D7A985B00EA417F /* MedicationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MedicationManager.swift; sourceTree = "<group>"; };
149161
C42C1C702D7AB84300EA417F /* MedicationViewUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MedicationViewUITests.swift; sourceTree = "<group>"; };
@@ -297,7 +309,6 @@
297309
653A2544283387FE005D4D48 = {
298310
isa = PBXGroup;
299311
children = (
300-
2FC94CD4298B0A1D009C8209 /* NeutroFeverGuard.xctestplan */,
301312
653A254F283387FE005D4D48 /* NeutroFeverGuard */,
302313
653A256028338800005D4D48 /* NeutroFeverGuardTests */,
303314
653A256A28338800005D4D48 /* NeutroFeverGuardUITests */,
@@ -320,11 +331,14 @@
320331
653A254F283387FE005D4D48 /* NeutroFeverGuard */ = {
321332
isa = PBXGroup;
322333
children = (
334+
B0DF8DFB2D7C114900581A00 /* HealthDataFetchable.swift */,
335+
B0FF4DD12D797FEA0076A043 /* NotificationManager.swift */,
323336
B0D4C9372D790F5E000643C7 /* LabResultsManager.swift */,
324337
C42C1C6A2D7A985B00EA417F /* MedicationManager.swift */,
325338
EB7D47E02D51433200CEDC78 /* HealthKitVisualizations */,
326339
F22393792D5D4092006C8EB4 /* DataError.swift */,
327340
C42D4DA42D5AC89500B2A169 /* LabView.swift */,
341+
B0C95E382D78D5F2007A4CA6 /* FeverMonitoring.swift */,
328342
C42C1B302D782A7100EA417F /* MedicationView.swift */,
329343
C4C0A78E2D518DB500F37EEC /* HelperFunc.swift */,
330344
F2E6898E2D52ED8500869C4F /* HealthKitService.swift */,
@@ -351,6 +365,7 @@
351365
653A256028338800005D4D48 /* NeutroFeverGuardTests */ = {
352366
isa = PBXGroup;
353367
children = (
368+
B0DF8DF62D7BFEF600581A00 /* FeverMonitorTests.swift */,
354369
653A256128338800005D4D48 /* NeutroFeverGuardTests.swift */,
355370
C4A367C72D73C1E1009923C9 /* DataErrorTests.swift */,
356371
C4C8A3B22D71204200F313AE /* HelperFuncTests.swift */,
@@ -600,6 +615,7 @@
600615
isa = PBXSourcesBuildPhase;
601616
buildActionMask = 2147483647;
602617
files = (
618+
B0C95E3A2D78D5F3007A4CA6 /* FeverMonitoring.swift in Sources */,
603619
F223937A2D5D4095006C8EB4 /* DataError.swift in Sources */,
604620
C49EC8532D5038AC005C3495 /* DataInputForm.swift in Sources */,
605621
2FE5DC4129EDD7EE004B9AB4 /* StorageKeys.swift in Sources */,
@@ -608,9 +624,11 @@
608624
2FE5DC3A29EDD7CA004B9AB4 /* Welcome.swift in Sources */,
609625
2FE5DC3829EDD7CA004B9AB4 /* InterestingModules.swift in Sources */,
610626
2FE5DC3529EDD7CA004B9AB4 /* Consent.swift in Sources */,
627+
B0FF4DD22D797FEA0076A043 /* NotificationManager.swift in Sources */,
611628
2FC975A82978F11A00BA99FE /* HomeView.swift in Sources */,
612629
A9DFE8A92ABE551400428242 /* AccountButton.swift in Sources */,
613630
C42C1C6B2D7A985B00EA417F /* MedicationManager.swift in Sources */,
631+
B0DF8DFC2D7C117800581A00 /* HealthDataFetchable.swift in Sources */,
614632
A9A3DCC82C75CBBD00FC9B69 /* FirebaseConfiguration.swift in Sources */,
615633
2FE5DC3729EDD7CA004B9AB4 /* OnboardingFlow.swift in Sources */,
616634
2F1AC9DF2B4E840E00C24973 /* NeutroFeverGuard.docc in Sources */,
@@ -640,8 +658,14 @@
640658
buildActionMask = 2147483647;
641659
files = (
642660
C4A367C82D73C1EC009923C9 /* DataErrorTests.swift in Sources */,
661+
B08E39F92D7C416000077929 /* FeverMonitoring.swift in Sources */,
643662
EB02C6612D5D52E90035AA89 /* NeutroFeverGuardTests.swift in Sources */,
663+
B0DF8DFD2D7C123200581A00 /* HealthDataFetchable.swift in Sources */,
664+
B0DF8DF72D7BFEFB00581A00 /* FeverMonitorTests.swift in Sources */,
665+
B03D0E482D7D24AA0024F2CA /* DataError.swift in Sources */,
644666
C4C8A3B32D71204200F313AE /* HelperFuncTests.swift in Sources */,
667+
B03D0E432D7D22700024F2CA /* HealthKitService.swift in Sources */,
668+
B03D0E462D7D24030024F2CA /* DataType.swift in Sources */,
645669
C49EC8382D5026EE005C3495 /* DataTypeTest.swift in Sources */,
646670
);
647671
runOnlyForDeploymentPostprocessing = 0;
@@ -971,7 +995,7 @@
971995
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
972996
CODE_SIGN_ENTITLEMENTS = "NeutroFeverGuard/Supporting Files/NeutroFeverGuard.entitlements";
973997
CODE_SIGN_IDENTITY = "Apple Development";
974-
CODE_SIGN_STYLE = Automatic;
998+
CODE_SIGN_STYLE = Manual;
975999
CURRENT_PROJECT_VERSION = 1;
9761000
DEVELOPMENT_ASSET_PATHS = "";
9771001
DEVELOPMENT_TEAM = "";

NeutroFeverGuard.xcodeproj/xcshareddata/xcschemes/NeutroFeverGuard.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
</CommandLineArgument>
8484
<CommandLineArgument
8585
argument = "--showOnboarding"
86-
isEnabled = "NO">
86+
isEnabled = "YES">
8787
</CommandLineArgument>
8888
<CommandLineArgument
8989
argument = "--skipOnboarding"

NeutroFeverGuard/Contacts/Contacts.swift

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,57 @@ struct Contacts: View {
1717
let contacts = [
1818
Contact(
1919
name: PersonNameComponents(
20-
givenName: "Leland",
21-
familyName: "Stanford"
20+
givenName: "Johannes",
21+
familyName: "Jung"
2222
),
23-
image: Image(systemName: "figure.wave.circle"), // swiftlint:disable:this accessibility_label_for_image
24-
title: "University Founder",
25-
description: String(localized: "LELAND_STANFORD_BIO"),
26-
organization: "Stanford University",
23+
image: Image(systemName: "stethoscope.circle.fill"), // swiftlint:disable:this accessibility_label_for_image
24+
title: "Dr. med.",
25+
organization: "Outpatient Clinic - ZIC, Klinikum recht der Isar TUM",
2726
address: {
2827
let address = CNMutablePostalAddress()
29-
address.country = "USA"
30-
address.state = "CA"
31-
address.postalCode = "94305"
32-
address.city = "Stanford"
33-
address.street = "450 Serra Mall"
28+
address.country = "Germany"
29+
address.postalCode = "81675"
30+
address.city = "München"
31+
address.street = "Ismaninger Str. 22"
3432
return address
3533
}(),
3634
contactOptions: [
37-
.call("+1 (650) 723-2300"),
38-
.text("+1 (650) 723-2300"),
39-
.email(addresses: ["[email protected]"]),
35+
.call("+49 89 4140 1022"),
36+
.text("+49 89 4140 1022"),
37+
.email(addresses: ["[email protected]"]),
4038
ContactOption(
4139
image: Image(systemName: "safari.fill"), // swiftlint:disable:this accessibility_label_for_image
4240
title: "Website",
4341
action: {
44-
if let url = URL(string: "https://stanford.edu") {
42+
if let url = URL(string: "https://hno.mri.tum.de/en/your-stay-us/outpatient-treatment") {
43+
UIApplication.shared.open(url)
44+
}
45+
}
46+
)
47+
]
48+
),
49+
Contact(
50+
name: PersonNameComponents(
51+
givenName: "Emergency",
52+
familyName: "Department"
53+
),
54+
image: Image(systemName: "cross.case.circle.fill"), // swiftlint:disable:this accessibility_label_for_image
55+
organization: "Zentrale Interdisziplinäre Notaufnahme TUM",
56+
address: {
57+
let address = CNMutablePostalAddress()
58+
address.country = "Germany"
59+
address.postalCode = "81675"
60+
address.city = "München"
61+
address.street = "Ismaninger Str. 22"
62+
return address
63+
}(),
64+
contactOptions: [
65+
.call("+49 89 4140 2222"),
66+
ContactOption(
67+
image: Image(systemName: "safari.fill"), // swiftlint:disable:this accessibility_label_for_image
68+
title: "Directions",
69+
action: {
70+
if let url = URL(string: "https://www.google.com/maps/dir/37.4200987,-122.1639501/Zentrale+Interdisziplinäre+Notaufnahme+tum/@2.9388026,-142.1016129,3z/data=!3m1!4b1!4m9!4m8!1m1!4e1!1m5!1m1!1s0x479e7582085e4a3f:0xd95085fa62e0faa4!2m2!1d11.5997627!2d48.135973?entry=ttu&g_ep=EgoyMDI1MDMwNC4wIKXMDSoASAFQAw%3D%3D") {
4571
UIApplication.shared.open(url)
4672
}
4773
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// This source file is part of the NeutroFeverGuard based on the Stanford Spezi Template Application project
3+
//
4+
// SPDX-FileCopyrightText: 2025 Stanford University
5+
//
6+
// SPDX-License-Identifier: MIT
7+
//
8+
9+
import Foundation
10+
import HealthKit
11+
12+
actor FeverMonitor: Sendable {
13+
static let shared = FeverMonitor()
14+
// private let healthStore = HKHealthStore()
15+
// private init() {}
16+
17+
private let healthDataFetcher: HealthDataFetchable
18+
19+
init(healthDataFetcher: HealthDataFetchable = HealthKitService()) {
20+
self.healthDataFetcher = healthDataFetcher
21+
}
22+
23+
func checkForFever() async -> Bool {
24+
do {
25+
let temperatures = try await healthDataFetcher.queryTemperatureData()
26+
27+
print("Fetched \(temperatures.count) temperature samples")
28+
29+
guard !temperatures.isEmpty else {
30+
print("No temperature samples found")
31+
return false
32+
}
33+
34+
for temp in temperatures {
35+
let tempFahrenheit = convertToFahrenheit(temp.quantity)
36+
print("Temperature: \(tempFahrenheit)°F at \(temp.startDate)")
37+
}
38+
39+
if let latest = temperatures.first {
40+
let latestTempFahrenheit = convertToFahrenheit(latest.quantity)
41+
print("Latest temperature: \(latestTempFahrenheit)°F")
42+
if latestTempFahrenheit >= 101.0 {
43+
print("Fever detected: Latest temperature is >= 101.0°F")
44+
return true
45+
}
46+
}
47+
48+
let allHighTemps = temperatures.allSatisfy { sample in
49+
let tempFahrenheit = convertToFahrenheit(sample.quantity)
50+
return tempFahrenheit >= 100.4
51+
}
52+
53+
if allHighTemps {
54+
print("Fever detected: All temperatures in the last hour are >= 100.4°F")
55+
return true
56+
} else {
57+
print("No fever detected")
58+
return false
59+
}
60+
} catch {
61+
print("Error fetching health data: \(error)")
62+
return false
63+
}
64+
}
65+
66+
private func convertToFahrenheit(_ quantity: HKQuantity) -> Double {
67+
if quantity.is(compatibleWith: HKUnit.degreeFahrenheit()) {
68+
return quantity.doubleValue(for: HKUnit.degreeFahrenheit())
69+
} else if quantity.is(compatibleWith: HKUnit.degreeCelsius()) {
70+
return celsiusToFahrenheit(quantity.doubleValue(for: HKUnit.degreeCelsius()))
71+
} else {
72+
print("Unknown temperature unit!")
73+
return 0.0 // Fallback in case of an unknown unit
74+
}
75+
}
76+
77+
private func celsiusToFahrenheit(_ tempCelsius: Double) -> Double {
78+
(tempCelsius * 9 / 5) + 32
79+
}
80+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// This source file is part of the NeutroFeverGuard based on the Stanford Spezi Template Application project
3+
//
4+
// SPDX-FileCopyrightText: 2025 Stanford University
5+
//
6+
// SPDX-License-Identifier: MIT
7+
//
8+
import HealthKit
9+
10+
protocol HealthDataFetchable: Sendable {
11+
func queryTemperatureData() async throws -> [HKQuantitySample]
12+
}

NeutroFeverGuard/HealthKitService.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Spezi
1212
import SpeziHealthKit
1313
import SpeziLocalStorage
1414

15-
actor HealthKitService: Module, EnvironmentAccessible {
15+
actor HealthKitService: Module, EnvironmentAccessible, HealthDataFetchable {
1616
internal let healthStore = HKHealthStore()
1717

1818
@MainActor
@@ -118,4 +118,35 @@ actor HealthKitService: Module, EnvironmentAccessible {
118118

119119
try await healthStore.save(correlation)
120120
}
121+
122+
func queryTemperatureData() async throws -> [HKQuantitySample] {
123+
guard let bodyTemperatureType = HKQuantityType.quantityType(forIdentifier: .bodyTemperature) else {
124+
print("HealthKit body temperature data is not available on this device.")
125+
return []
126+
}
127+
128+
let now = Date()
129+
guard let hourAgo = Calendar.current.date(byAdding: .hour, value: -1, to: now) else {
130+
return []
131+
}
132+
133+
let predicate = HKQuery.predicateForSamples(withStart: hourAgo, end: now)
134+
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
135+
136+
return try await withCheckedThrowingContinuation { continuation in
137+
let query = HKSampleQuery(
138+
sampleType: bodyTemperatureType,
139+
predicate: predicate,
140+
limit: HKObjectQueryNoLimit,
141+
sortDescriptors: [sortDescriptor]
142+
) { _, samples, error in
143+
if let error = error {
144+
continuation.resume(throwing: error)
145+
return
146+
}
147+
continuation.resume(returning: samples as? [HKQuantitySample] ?? [])
148+
}
149+
healthStore.execute(query)
150+
}
151+
}
121152
}

NeutroFeverGuard/HomeView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ struct HomeView: View {
2525
@AppStorage(StorageKeys.tabViewCustomization) private var tabViewCustomization = TabViewCustomization()
2626

2727
@State private var presentingAccount = false
28-
29-
28+
3029
var body: some View {
3130
TabView(selection: $selectedTab) {
3231
// Dashboard tab (HKVisualization)

NeutroFeverGuard/LabResultsManager.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import SwiftUI
1313

1414

1515
@Observable
16-
@MainActor
1716
class LabResultsManager: Module, EnvironmentAccessible {
1817
var latestRecordedTime: String = "None"
1918
var labRecords: [LabEntry] = []
@@ -66,11 +65,13 @@ class LabResultsManager: Module, EnvironmentAccessible {
6665
}
6766
}
6867

68+
@MainActor
6969
func addLabEntry(_ newEntry: LabEntry) {
7070
labRecords.append(newEntry)
7171
saveLabResults()
7272
}
7373

74+
@MainActor
7475
func deleteLabEntry(at index: Int) {
7576
guard labRecords.indices.contains(index)
7677
else { return }
@@ -84,7 +85,8 @@ class LabResultsManager: Module, EnvironmentAccessible {
8485
// labRecords[index] = updatedEntry
8586
// saveLabResults()
8687
// }
87-
88+
89+
@MainActor
8890
private func saveLabResults() {
8991
if FeatureFlags.mockLabData {
9092
mockLabData = labRecords

NeutroFeverGuard/NeutroFeverGuard.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import SwiftUI
1616
struct NeutroFeverGuard: App {
1717
@UIApplicationDelegateAdaptor(NeutroFeverGuardDelegate.self) var appDelegate
1818
@AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false
19-
2019

2120
var body: some Scene {
2221
WindowGroup {

NeutroFeverGuard/NeutroFeverGuardDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class NeutroFeverGuardDelegate: SpeziAppDelegate {
5656
OnboardingDataSource()
5757
LocalStorage()
5858
Notifications()
59+
NotificationManager()
5960
LabResultsManager()
6061
MedicationManager()
6162
HealthKitService()

0 commit comments

Comments
 (0)