FetchGlucoseManager.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import Combine
  2. import Foundation
  3. import SwiftDate
  4. import Swinject
  5. protocol FetchGlucoseManager: SourceInfoProvider {}
  6. final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
  7. private let processQueue = DispatchQueue(label: "BaseGlucoseManager.processQueue")
  8. @Injected() var glucoseStorage: GlucoseStorage!
  9. @Injected() var nightscoutManager: NightscoutManager!
  10. @Injected() var apsManager: APSManager!
  11. @Injected() var settingsManager: SettingsManager!
  12. @Injected() var libreTransmitter: LibreTransmitterSource!
  13. @Injected() var healthKitManager: HealthKitManager!
  14. @Injected() var deviceDataManager: DeviceDataManager!
  15. private var lifetime = Lifetime()
  16. private let timer = DispatchTimer(timeInterval: 1.minutes.timeInterval)
  17. private lazy var dexcomSource = DexcomSource(glucoseStorage: glucoseStorage)
  18. private lazy var dexcomSourceG7 = DexcomSourceG7(glucoseStorage: glucoseStorage)
  19. private lazy var simulatorSource = GlucoseSimulatorSource()
  20. init(resolver: Resolver) {
  21. injectServices(resolver)
  22. updateGlucoseSource()
  23. subscribe()
  24. }
  25. var glucoseSource: GlucoseSource!
  26. private func updateGlucoseSource() {
  27. switch settingsManager.settings.cgm {
  28. case .xdrip:
  29. glucoseSource = AppGroupSource(from: "xDrip")
  30. case .dexcomG5,
  31. .dexcomG6:
  32. glucoseSource = dexcomSource
  33. case .dexcomG7:
  34. glucoseSource = dexcomSourceG7
  35. case .nightscout:
  36. glucoseSource = nightscoutManager
  37. case .simulator:
  38. glucoseSource = simulatorSource
  39. case .libreTransmitter:
  40. glucoseSource = libreTransmitter
  41. case .glucoseDirect:
  42. glucoseSource = AppGroupSource(from: "GlucoseDirect")
  43. case .enlite:
  44. glucoseSource = deviceDataManager
  45. }
  46. if settingsManager.settings.cgm != .libreTransmitter {
  47. libreTransmitter.manager = nil
  48. }
  49. }
  50. private func subscribe() {
  51. timer.publisher
  52. .receive(on: processQueue)
  53. .flatMap { date -> AnyPublisher<(Date, Date, [BloodGlucose], [BloodGlucose]), Never> in
  54. debug(.nightscout, "FetchGlucoseManager heartbeat")
  55. // debug(.nightscout, "Start fetching glucose")
  56. self.updateGlucoseSource()
  57. return Publishers.CombineLatest4(
  58. Just(date),
  59. Just(self.glucoseStorage.syncDate()),
  60. self.glucoseSource.fetch(self.timer),
  61. self.healthKitManager.fetch(nil)
  62. )
  63. .eraseToAnyPublisher()
  64. }
  65. .sink { date, syncDate, glucose, glucoseFromHealth in
  66. debug(.nightscout, "FETCHGLUCOSE : SyncDate is \(syncDate)")
  67. let allGlucose = glucose + glucoseFromHealth
  68. guard allGlucose.isNotEmpty else { return }
  69. // Because of Spike dosn't respect a date query
  70. let filteredByDate = allGlucose.filter { $0.dateString > syncDate }
  71. let filtered = self.glucoseStorage.filterTooFrequentGlucose(filteredByDate, at: syncDate)
  72. guard filtered.isNotEmpty else { return }
  73. debug(.nightscout, "New glucose found")
  74. self.glucoseStorage.storeGlucose(filtered)
  75. self.apsManager.heartbeat(date: date)
  76. self.nightscoutManager.uploadGlucose()
  77. let glucoseForHealth = filteredByDate.filter { !glucoseFromHealth.contains($0) }
  78. guard glucoseForHealth.isNotEmpty else { return }
  79. self.healthKitManager.saveIfNeeded(bloodGlucose: glucoseForHealth)
  80. }
  81. .store(in: &lifetime)
  82. timer.fire()
  83. timer.resume()
  84. UserDefaults.standard
  85. .publisher(for: \.dexcomTransmitterID)
  86. .removeDuplicates()
  87. .sink { id in
  88. guard [.dexcomG5, .dexcomG6].contains(self.settingsManager.settings.cgm) else { return }
  89. if id != self.dexcomSource.transmitterID {
  90. self.dexcomSource = DexcomSource(glucoseStorage: self.glucoseStorage)
  91. }
  92. }
  93. .store(in: &lifetime)
  94. }
  95. func sourceInfo() -> [String: Any]? {
  96. glucoseSource.sourceInfo()
  97. }
  98. }
  99. extension UserDefaults {
  100. @objc var dexcomTransmitterID: String? {
  101. get {
  102. string(forKey: "DexcomSource.transmitterID")?.nonEmpty
  103. }
  104. set {
  105. set(newValue, forKey: "DexcomSource.transmitterID")
  106. }
  107. }
  108. }