FetchGlucoseManager.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. private var lifetime = Lifetime()
  14. private let timer = DispatchTimer(timeInterval: 1.minutes.timeInterval)
  15. private lazy var appGroupSource = AppGroupSource()
  16. private lazy var dexcomSource = DexcomSource()
  17. private lazy var simulatorSource = GlucoseSimulatorSource()
  18. init(resolver: Resolver) {
  19. injectServices(resolver)
  20. updateGlucoseSource()
  21. subscribe()
  22. }
  23. var glucoseSource: GlucoseSource!
  24. private func updateGlucoseSource() {
  25. switch settingsManager.settings.cgm {
  26. case .xdrip:
  27. glucoseSource = appGroupSource
  28. case .dexcomG5,
  29. .dexcomG6:
  30. glucoseSource = dexcomSource
  31. case .nightscout:
  32. glucoseSource = nightscoutManager
  33. case .simulator:
  34. glucoseSource = simulatorSource
  35. case .libreTransmitter:
  36. glucoseSource = libreTransmitter
  37. case .glucoseDirect:
  38. glucoseSource = appGroupSource
  39. }
  40. if settingsManager.settings.cgm != .libreTransmitter {
  41. libreTransmitter.manager = nil
  42. }
  43. }
  44. private func subscribe() {
  45. timer.publisher
  46. .receive(on: processQueue)
  47. .flatMap { date -> AnyPublisher<(Date, Date, [BloodGlucose]), Never> in
  48. debug(.nightscout, "FetchGlucoseManager heartbeat")
  49. debug(.nightscout, "Start fetching glucose")
  50. self.updateGlucoseSource()
  51. return Publishers.CombineLatest3(
  52. Just(date),
  53. Just(self.glucoseStorage.syncDate()),
  54. self.glucoseSource.fetch()
  55. )
  56. .eraseToAnyPublisher()
  57. }
  58. .sink { date, syncDate, glucose in
  59. // Because of Spike dosn't respect a date query
  60. let filteredByDate = glucose.filter { $0.dateString > syncDate }
  61. let filtered = self.glucoseStorage.filterTooFrequentGlucose(filteredByDate, at: syncDate)
  62. if !filtered.isEmpty {
  63. debug(.nightscout, "New glucose found")
  64. self.glucoseStorage.storeGlucose(filtered)
  65. self.apsManager.heartbeat(date: date, force: false)
  66. self.nightscoutManager.uploadGlucose()
  67. }
  68. }
  69. .store(in: &lifetime)
  70. timer.fire()
  71. timer.resume()
  72. UserDefaults.standard
  73. .publisher(for: \.dexcomTransmitterID)
  74. .removeDuplicates()
  75. .sink { id in
  76. guard [.dexcomG5, .dexcomG6].contains(self.settingsManager.settings.cgm) else { return }
  77. if id != self.dexcomSource.transmitterID {
  78. self.dexcomSource = DexcomSource()
  79. }
  80. }
  81. .store(in: &lifetime)
  82. }
  83. func sourceInfo() -> [String: Any]? {
  84. glucoseSource.sourceInfo()
  85. }
  86. }
  87. extension UserDefaults {
  88. @objc var dexcomTransmitterID: String? {
  89. get {
  90. string(forKey: "DexcomSource.transmitterID")?.nonEmpty
  91. }
  92. set {
  93. set(newValue, forKey: "DexcomSource.transmitterID")
  94. }
  95. }
  96. }