@@ -10,6 +10,7 @@ import Foundation
1010import HealthKit
1111import LoopKit
1212import LoopCore
13+ import Combine
1314
1415
1516final class LoopDataManager {
@@ -33,6 +34,8 @@ final class LoopDataManager {
3334
3435 private let logger : CategoryLogger
3536
37+ private var loopSubscription : AnyCancellable ?
38+
3639 // References to registered notification center observers
3740 private var notificationObservers : [ Any ] = [ ]
3841
@@ -636,57 +639,77 @@ extension LoopDataManager {
636639 /// Executes an analysis of the current data, and recommends an adjustment to the current
637640 /// temporary basal rate.
638641 func loop( ) {
639- self . dataAccessQueue. async {
640- self . logger. default ( " Loop running " )
641- NotificationCenter . default. post ( name: . LoopRunning, object: self )
642+ let updatePublisher = Deferred {
643+ Future < ( ) , Error > { promise in
644+ do {
645+ try self . update ( )
646+ promise ( . success( ( ) ) )
647+ } catch let error {
648+ promise ( . failure( error) )
649+ }
650+ }
651+ }
652+ . subscribe ( on: dataAccessQueue)
653+ . eraseToAnyPublisher ( )
642654
643- self . lastLoopError = nil
644- let startDate = Date ( )
655+ let enactBolusPuiblisher = Deferred {
656+ Future < Bool , Error > { promise in
657+ guard self . settings. dosingEnabled, self . settings. microbolusesEnabled else {
658+ return promise ( . success( false ) )
659+ }
660+ self . calculateAndEnactMicroBolusIfNeeded { enacted, error in
661+ if let error = error {
662+ promise ( . failure( error) )
663+ }
664+ promise ( . success( enacted) )
665+ }
666+ }
667+ }
668+ . subscribe ( on: dataAccessQueue)
669+ . eraseToAnyPublisher ( )
645670
646- do {
647- try self . update ( )
671+ let setBasalPublisher = Deferred {
672+ Future < ( ) , Error > { promise in
673+ guard self . settings. dosingEnabled else {
674+ return promise ( . success( ( ) ) )
675+ }
648676
649- if self . settings. dosingEnabled {
650- self . setRecommendedTempBasal { ( error) -> Void in
651- self . lastLoopError = error
652-
653- if let error = error {
654- self . logger. error ( error)
655- } else {
656- if self . settings. microbolusesEnabled {
657- self . calculateAndEnactMicroBolusIfNeeded { error in
658- if let error = error {
659- self . lastLoopError = error
660- self . logger. error ( error)
661- } else {
662- self . loopDidComplete ( date: Date ( ) , duration: - startDate. timeIntervalSinceNow)
663- }
664-
665- self . logger. default ( " Loop ended " )
666- self . notify ( forChange: . tempBasal)
667-
668- return
669- }
670- } else {
671- self . loopDidComplete ( date: Date ( ) , duration: - startDate. timeIntervalSinceNow)
672- }
673- }
674- self . logger. default ( " Loop ended " )
675- self . notify ( forChange: . tempBasal)
677+ self . setRecommendedTempBasal { error in
678+ if let error = error {
679+ promise ( . failure( error) )
676680 }
677-
678- // Delay the notification until we know the result of the temp basal
679- return
680- } else {
681- self . loopDidComplete ( date: Date ( ) , duration: - startDate. timeIntervalSinceNow)
681+ promise ( . success( ( ) ) )
682682 }
683- } catch let error {
684- self . lastLoopError = error
685- }
686683
687- self . logger. default ( " Loop ended " )
688- self . notify ( forChange: . tempBasal)
684+ }
689685 }
686+ . subscribe ( on: dataAccessQueue)
687+ . eraseToAnyPublisher ( )
688+
689+ logger. default ( " Loop running " )
690+ NotificationCenter . default. post ( name: . LoopRunning, object: self )
691+
692+ loopSubscription? . cancel ( )
693+
694+ let startDate = Date ( )
695+ loopSubscription = updatePublisher
696+ . flatMap { _ in setBasalPublisher }
697+ . flatMap { _ in enactBolusPuiblisher }
698+ . receive ( on: dataAccessQueue)
699+ . sink (
700+ receiveCompletion: { completion in
701+ switch completion {
702+ case . finished:
703+ self . loopDidComplete ( date: Date ( ) , duration: - startDate. timeIntervalSinceNow)
704+ case let . failure( error) :
705+ self . lastLoopError = nil
706+ self . logger. error ( error)
707+ }
708+ self . logger. default ( " Loop ended " )
709+ self . notify ( forChange: . tempBasal)
710+ } ,
711+ receiveValue: { _ in }
712+ )
690713 }
691714
692715 /// - Throws:
@@ -1047,38 +1070,38 @@ extension LoopDataManager {
10471070 }
10481071
10491072 /// *This method should only be called from the `dataAccessQueue`*
1050- private func calculateAndEnactMicroBolusIfNeeded( _ completion: @escaping ( _ error: Error ? ) -> Void ) {
1073+ private func calculateAndEnactMicroBolusIfNeeded( _ completion: @escaping ( _ enacted : Bool , _ error: Error ? ) -> Void ) {
10511074 dispatchPrecondition ( condition: . onQueue( dataAccessQueue) )
10521075
10531076 guard settings. dosingEnabled && settings. microbolusesEnabled else {
10541077 logger. debug ( " Closed loop or microboluses disabled. Cancel microbolus calculation. " )
1055- completion ( nil )
1078+ completion ( false , nil )
10561079 return
10571080 }
10581081
10591082 let startDate = Date ( )
10601083
10611084 guard let recommendedBolus = recommendedBolus else {
10621085 logger. debug ( " No recommended bolus. Cancel microbolus calculation. " )
1063- completion ( nil )
1086+ completion ( false , nil )
10641087 return
10651088 }
10661089
10671090 guard abs ( recommendedBolus. date. timeIntervalSinceNow) < TimeInterval ( minutes: 5 ) else {
1068- completion ( LoopError . recommendationExpired ( date: recommendedBolus. date) )
1091+ completion ( false , LoopError . recommendationExpired ( date: recommendedBolus. date) )
10691092 return
10701093 }
10711094
10721095 guard let currentBasalRate = basalRateScheduleApplyingOverrideHistory? . value ( at: startDate) else {
10731096 logger. debug ( " Basal rates not configured. Cancel microbolus calculation. " )
1074- completion ( nil )
1097+ completion ( false , nil )
10751098 return
10761099 }
10771100
10781101 let insulinReq = recommendedBolus. recommendation. amount
10791102 guard insulinReq > 0 else {
10801103 logger. debug ( " No microbolus needed. " )
1081- completion ( nil )
1104+ completion ( false , nil )
10821105 return
10831106 }
10841107
@@ -1095,10 +1118,10 @@ extension LoopDataManager {
10951118 self . delegate? . loopDataManager ( self , didRecommendMicroBolus: recomendation) { [ weak self] error in
10961119 if let error = error {
10971120 self ? . logger. debug ( " Microbolus failed: \( error. localizedDescription) " )
1098- completion ( error)
1121+ completion ( false , error)
10991122 } else {
11001123 self ? . logger. debug ( " Microbolus enacted " )
1101- completion ( nil )
1124+ completion ( true , nil )
11021125 }
11031126 }
11041127 }
0 commit comments