@@ -67,6 +67,46 @@ final class DeviceDataManager {
6767 UserDefaults . appGroup? . pumpManagerRawValue = pumpManager? . rawValue
6868 }
6969 }
70+
71+ // MARK: Stores
72+ let healthStore : HKHealthStore
73+
74+ let carbStore : CarbStore
75+
76+ let doseStore : DoseStore
77+
78+ let glucoseStore : GlucoseStore
79+
80+ private let cacheStore : PersistenceController
81+
82+ let dosingDecisionStore : DosingDecisionStore
83+
84+ let settingsStore : SettingsStore
85+
86+ /// All the HealthKit types to be read and shared by stores
87+ private var sampleTypes : Set < HKSampleType > {
88+ return Set ( [
89+ glucoseStore. sampleType,
90+ carbStore. sampleType,
91+ doseStore. sampleType,
92+ ] . compactMap { $0 } )
93+ }
94+
95+ /// True if any stores require HealthKit authorization
96+ var authorizationRequired : Bool {
97+ return glucoseStore. authorizationRequired ||
98+ carbStore. authorizationRequired ||
99+ doseStore. authorizationRequired
100+ }
101+
102+ /// True if the user has explicitly denied access to any stores' HealthKit types
103+ private var sharingDenied : Bool {
104+ return glucoseStore. sharingDenied ||
105+ carbStore. sharingDenied ||
106+ doseStore. sharingDenied
107+ }
108+
109+ // MARK: Services
70110
71111 private( set) var servicesManager : ServicesManager !
72112
@@ -115,6 +155,49 @@ final class DeviceDataManager {
115155 self . pluginManager = pluginManager
116156 self . alertManager = alertManager
117157
158+ self . healthStore = HKHealthStore ( )
159+ self . cacheStore = PersistenceController . controllerInAppGroupDirectory ( )
160+
161+ let absorptionTimes = LoopSettings . defaultCarbAbsorptionTimes
162+ let sensitivitySchedule = UserDefaults . appGroup? . insulinSensitivitySchedule
163+ let overrideHistory = UserDefaults . appGroup? . overrideHistory ?? TemporaryScheduleOverrideHistory . init ( )
164+
165+ self . carbStore = CarbStore (
166+ healthStore: healthStore,
167+ observeHealthKitSamplesFromOtherApps: FeatureFlags . observeHealthKitSamplesFromOtherApps,
168+ cacheStore: cacheStore,
169+ cacheLength: localCacheDuration,
170+ defaultAbsorptionTimes: absorptionTimes,
171+ observationInterval: absorptionTimes. slow * 2 ,
172+ carbRatioSchedule: UserDefaults . appGroup? . carbRatioSchedule,
173+ insulinSensitivitySchedule: sensitivitySchedule,
174+ overrideHistory: overrideHistory,
175+ carbAbsorptionModel: FeatureFlags . nonlinearCarbModelEnabled ? . nonlinear : . linear
176+ )
177+
178+ self . doseStore = DoseStore (
179+ healthStore: healthStore,
180+ observeHealthKitSamplesFromOtherApps: FeatureFlags . observeHealthKitSamplesFromOtherApps,
181+ cacheStore: cacheStore,
182+ cacheLength: localCacheDuration,
183+ insulinModel: UserDefaults . appGroup? . insulinModelSettings? . model,
184+ basalProfile: UserDefaults . appGroup? . basalRateSchedule,
185+ insulinSensitivitySchedule: sensitivitySchedule,
186+ overrideHistory: overrideHistory,
187+ lastPumpEventsReconciliation: pumpManager? . lastReconciliation
188+ )
189+
190+ self . glucoseStore = GlucoseStore (
191+ healthStore: healthStore,
192+ observeHealthKitSamplesFromOtherApps: FeatureFlags . observeHealthKitSamplesFromOtherApps,
193+ cacheStore: cacheStore,
194+ cacheLength: localCacheDuration,
195+ observationInterval: . hours( 24 )
196+ )
197+
198+ self . dosingDecisionStore = DosingDecisionStore ( store: cacheStore, expireAfter: localCacheDuration)
199+ self . settingsStore = SettingsStore ( store: cacheStore, expireAfter: localCacheDuration)
200+
118201 bluetoothStateManager. addBluetoothStateObserver ( self )
119202
120203 if let pumpManagerRawValue = UserDefaults . appGroup? . pumpManagerRawValue {
@@ -136,16 +219,23 @@ final class DeviceDataManager {
136219 basalDeliveryState: pumpManager? . status. basalDeliveryState,
137220 lastPumpEventsReconciliation: pumpManager? . lastReconciliation,
138221 analyticsServicesManager: analyticsServicesManager,
139- localCacheDuration: localCacheDuration
222+ localCacheDuration: localCacheDuration,
223+ doseStore: doseStore,
224+ glucoseStore: glucoseStore,
225+ carbStore: carbStore,
226+ dosingDecisionStore: dosingDecisionStore,
227+ settingsStore: settingsStore
140228 )
229+ cacheStore. delegate = loopManager
230+
141231 watchManager = WatchDataManager ( deviceManager: self )
142232
143233 let remoteDataServicesManager = RemoteDataServicesManager (
144- carbStore: loopManager . carbStore,
145- doseStore: loopManager . doseStore,
146- dosingDecisionStore: loopManager . dosingDecisionStore,
147- glucoseStore: loopManager . glucoseStore,
148- settingsStore: loopManager . settingsStore
234+ carbStore: carbStore,
235+ doseStore: doseStore,
236+ dosingDecisionStore: dosingDecisionStore,
237+ glucoseStore: glucoseStore,
238+ settingsStore: settingsStore
149239 )
150240
151241 servicesManager = ServicesManager (
@@ -156,18 +246,17 @@ final class DeviceDataManager {
156246 dataManager: loopManager
157247 )
158248
159-
160249 if FeatureFlags . scenariosEnabled {
161250 testingScenariosManager = LocalTestingScenariosManager ( deviceManager: self )
162251 }
163252
164253 loopManager. delegate = self
165254
166- loopManager . carbStore. delegate = self
167- loopManager . doseStore. delegate = self
168- loopManager . dosingDecisionStore. delegate = self
169- loopManager . glucoseStore. delegate = self
170- loopManager . settingsStore. delegate = self
255+ carbStore. delegate = self
256+ doseStore. delegate = self
257+ dosingDecisionStore. delegate = self
258+ glucoseStore. delegate = self
259+ settingsStore. delegate = self
171260
172261 setupPump ( )
173262 setupCGM ( )
@@ -257,6 +346,21 @@ final class DeviceDataManager {
257346
258347 return Manager . init ( rawState: rawState) as? CGMManagerUI
259348 }
349+
350+ // Get HealthKit authorization for all of the stores
351+ func authorize( _ completion: @escaping ( ) -> Void ) {
352+ // Authorize all types at once for simplicity
353+ healthStore. requestAuthorization ( toShare: sampleTypes, read: sampleTypes) { ( success, error) in
354+ if success {
355+ // Call the individual authorization methods to trigger query creation
356+ self . carbStore. authorize ( toShare: true , { _ in } )
357+ self . doseStore. insulinDeliveryStore. authorize ( toShare: true , { _ in } )
358+ self . glucoseStore. authorize ( toShare: true , { _ in } )
359+ }
360+
361+ completion ( )
362+ }
363+ }
260364
261365 func generateDiagnosticReport( _ completion: @escaping ( _ report: String ) -> Void ) {
262366 self . loopManager. generateDiagnosticReport { ( loopReport) in
@@ -288,6 +392,8 @@ final class DeviceDataManager {
288392 " * lastError: \( String ( describing: self . lastError) ) " ,
289393 " * lastBLEDrivenUpdate: \( self . lastBLEDrivenUpdate) " ,
290394 " " ,
395+ " cacheStore: \( String ( reflecting: self . cacheStore) ) " ,
396+ " " ,
291397 self . cgmManager != nil ? String ( reflecting: self . cgmManager!) : " cgmManager: nil " ,
292398 " " ,
293399 self . pumpManager != nil ? String ( reflecting: self . pumpManager!) : " pumpManager: nil " ,
@@ -317,7 +423,7 @@ private extension DeviceDataManager {
317423 cgmManager? . cgmManagerDelegate = self
318424 cgmManager? . delegateQueue = queue
319425
320- loopManager . glucoseStore. managedDataInterval = cgmManager? . managedDataInterval
426+ glucoseStore. managedDataInterval = cgmManager? . managedDataInterval
321427
322428 updatePumpManagerBLEHeartbeatPreference ( )
323429 if let cgmManager = cgmManager {
@@ -334,12 +440,12 @@ private extension DeviceDataManager {
334440 pumpManager? . pumpManagerDelegate = self
335441 pumpManager? . delegateQueue = queue
336442
337- loopManager . doseStore. device = pumpManager? . status. device
443+ doseStore. device = pumpManager? . status. device
338444 pumpManagerHUDProvider = pumpManager? . hudProvider ( insulinTintColor: . insulinTintColor, guidanceColors: . default)
339445
340446 // Proliferate PumpModel preferences to DoseStore
341447 if let pumpRecordsBasalProfileStartEvents = pumpManager? . pumpRecordsBasalProfileStartEvents {
342- loopManager ? . doseStore. pumpRecordsBasalProfileStartEvents = pumpRecordsBasalProfileStartEvents
448+ doseStore. pumpRecordsBasalProfileStartEvents = pumpRecordsBasalProfileStartEvents
343449 }
344450 if let pumpManager = pumpManager {
345451 alertManager? . addAlertResponder ( managerIdentifier: pumpManager. managerIdentifier,
@@ -453,7 +559,7 @@ extension DeviceDataManager: CGMManagerDelegate {
453559
454560 func startDateToFilterNewData( for manager: CGMManager ) -> Date ? {
455561 dispatchPrecondition ( condition: . onQueue( queue) )
456- return loopManager . glucoseStore. latestGlucose? . startDate
562+ return glucoseStore. latestGlucose? . startDate
457563 }
458564
459565 func cgmManagerDidUpdateState( _ manager: CGMManager ) {
@@ -542,7 +648,7 @@ extension DeviceDataManager: PumpManagerDelegate {
542648 dispatchPrecondition ( condition: . onQueue( queue) )
543649 log. default ( " PumpManager:%{public}@ did update status: %{public}@ " , String ( describing: type ( of: pumpManager) ) , String ( describing: status) )
544650
545- loopManager . doseStore. device = status. device
651+ doseStore. device = status. device
546652
547653 if let newBatteryValue = status. pumpBatteryChargeRemaining {
548654 if newBatteryValue == 0 {
@@ -569,7 +675,7 @@ extension DeviceDataManager: PumpManagerDelegate {
569675
570676 log. default ( " PumpManager:%{public}@ will deactivate " , String ( describing: type ( of: pumpManager) ) )
571677
572- loopManager . doseStore. resetPumpData ( )
678+ doseStore. resetPumpData ( completion : nil )
573679 DispatchQueue . main. async {
574680 self . pumpManager = nil
575681 }
@@ -579,15 +685,15 @@ extension DeviceDataManager: PumpManagerDelegate {
579685 dispatchPrecondition ( condition: . onQueue( queue) )
580686 log. default ( " PumpManager:%{public}@ did update pumpRecordsBasalProfileStartEvents to %{public}@ " , String ( describing: type ( of: pumpManager) ) , String ( describing: pumpRecordsBasalProfileStartEvents) )
581687
582- loopManager . doseStore. pumpRecordsBasalProfileStartEvents = pumpRecordsBasalProfileStartEvents
688+ doseStore. pumpRecordsBasalProfileStartEvents = pumpRecordsBasalProfileStartEvents
583689 }
584690
585691 func pumpManager( _ pumpManager: PumpManager , didError error: PumpManagerError ) {
586692 dispatchPrecondition ( condition: . onQueue( queue) )
587693 log. error ( " PumpManager:%{public}@ did error: %{public}@ " , String ( describing: type ( of: pumpManager) ) , String ( describing: error) )
588694
589695 setLastError ( error: error)
590- loopManager. storeDosingDecision ( withError: error)
696+ loopManager. storeDosingDecision ( withDate : Date ( ) , withError: error)
591697 }
592698
593699 func pumpManager( _ pumpManager: PumpManager , hasNewPumpEvents events: [ NewPumpEvent ] , lastReconciliation: Date ? , completion: @escaping ( _ error: Error ? ) -> Void ) {
@@ -653,7 +759,7 @@ extension DeviceDataManager: PumpManagerDelegate {
653759
654760 func startDateToFilterNewPumpEvents( for manager: PumpManager ) -> Date {
655761 dispatchPrecondition ( condition: . onQueue( queue) )
656- return loopManager . doseStore. pumpEventQueryAfterDate
762+ return doseStore. pumpEventQueryAfterDate
657763 }
658764}
659765
@@ -721,16 +827,16 @@ extension DeviceDataManager {
721827 }
722828
723829 let devicePredicate = HKQuery . predicateForObjects ( from: [ testingPumpManager. testingDevice] )
724- let doseStore = loopManager. doseStore
725830 let insulinDeliveryStore = doseStore. insulinDeliveryStore
831+
726832 let healthStore = insulinDeliveryStore. healthStore
727833 doseStore. resetPumpData { doseStoreError in
728834 guard doseStoreError == nil else {
729835 completion ? ( doseStoreError!)
730836 return
731837 }
732838
733- healthStore. deleteObjects ( of: doseStore. sampleType!, predicate: devicePredicate) { success, deletedObjectCount, error in
839+ healthStore. deleteObjects ( of: self . doseStore. sampleType!, predicate: devicePredicate) { success, deletedObjectCount, error in
734840 if success {
735841 insulinDeliveryStore. test_lastBasalEndDate = nil
736842 }
@@ -750,7 +856,7 @@ extension DeviceDataManager {
750856 }
751857
752858 let predicate = HKQuery . predicateForObjects ( from: [ testingCGMManager. testingDevice] )
753- loopManager . glucoseStore. purgeGlucoseSamples ( matchingCachePredicate: nil , healthKitPredicate: predicate) { success, count, error in
859+ glucoseStore. purgeGlucoseSamples ( matchingCachePredicate: nil , healthKitPredicate: predicate) { success, count, error in
754860 completion ? ( error)
755861 }
756862 }
0 commit comments