FetchGlucoseManager.swift 3.9 KB

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