FetchGlucoseManager.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import Combine
  2. import Foundation
  3. import SwiftDate
  4. import Swinject
  5. protocol FetchGlucoseManager {}
  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. private var lifetime = Lifetime()
  12. private let timer = DispatchTimer(timeInterval: 1.minutes.timeInterval)
  13. init(resolver: Resolver) {
  14. injectServices(resolver)
  15. subscribe()
  16. }
  17. private func subscribe() {
  18. timer.publisher
  19. .receive(on: processQueue)
  20. .flatMap { date -> AnyPublisher<(Date, Date, [BloodGlucose]), Never> in
  21. debug(.nightscout, "FetchGlucoseManager heartbeat")
  22. debug(.nightscout, "Start fetching glucose")
  23. return Publishers.CombineLatest3(
  24. Just(date),
  25. Just(self.glucoseStorage.syncDate()),
  26. Publishers.CombineLatest(
  27. self.nightscoutManager.fetchGlucose(),
  28. self.fetchGlucoseFromSharedGroup()
  29. )
  30. .map { [$0, $1].flatMap { $0 } }
  31. .eraseToAnyPublisher()
  32. )
  33. .eraseToAnyPublisher()
  34. }
  35. .sink { date, syncDate, glucose in
  36. // Because of Spike dosn't respect a date query
  37. let filteredByDate = glucose.filter { $0.dateString > syncDate }
  38. let filtered = self.glucoseStorage.filterTooFrequentGlucose(filteredByDate, at: syncDate)
  39. if !filtered.isEmpty {
  40. debug(.nightscout, "New glucose found")
  41. self.glucoseStorage.storeGlucose(filtered)
  42. self.apsManager.heartbeat(date: date, force: false)
  43. }
  44. }
  45. .store(in: &lifetime)
  46. timer.fire()
  47. timer.resume()
  48. }
  49. private func fetchGlucoseFromSharedGroup() -> AnyPublisher<[BloodGlucose], Never> {
  50. guard let suiteName = Bundle.main.appGroupSuiteName,
  51. let sharedDefaults = UserDefaults(suiteName: suiteName)
  52. else {
  53. return Just([]).eraseToAnyPublisher()
  54. }
  55. return Just(fetchLastBGs(60, sharedDefaults)).eraseToAnyPublisher()
  56. }
  57. private func fetchLastBGs(_ count: Int, _ sharedDefaults: UserDefaults) -> [BloodGlucose] {
  58. guard let sharedData = sharedDefaults.data(forKey: "latestReadings") else {
  59. return []
  60. }
  61. let decoded = try? JSONSerialization.jsonObject(with: sharedData, options: [])
  62. guard let sgvs = decoded as? [AnyObject] else {
  63. return []
  64. }
  65. var results: [BloodGlucose] = []
  66. for sgv in sgvs.prefix(count) {
  67. guard
  68. let glucose = sgv["Value"] as? Int,
  69. let direction = sgv["direction"] as? String,
  70. let timestamp = sgv["DT"] as? String,
  71. let date = parseDate(timestamp)
  72. else { continue }
  73. results.append(
  74. BloodGlucose(
  75. _id: UUID().uuidString,
  76. sgv: glucose,
  77. direction: BloodGlucose.Direction(rawValue: direction),
  78. date: Decimal(Int(date.timeIntervalSince1970 * 1000)),
  79. dateString: date,
  80. filtered: nil,
  81. noise: nil,
  82. glucose: glucose
  83. )
  84. )
  85. }
  86. return results
  87. }
  88. private func parseDate(_ timestamp: String) -> Date? {
  89. // timestamp looks like "/Date(1462404576000)/"
  90. guard let re = try? NSRegularExpression(pattern: "\\((.*)\\)"),
  91. let match = re.firstMatch(in: timestamp, range: NSMakeRange(0, timestamp.count))
  92. else {
  93. return nil
  94. }
  95. let matchRange = match.range(at: 1)
  96. let epoch = Double((timestamp as NSString).substring(with: matchRange))! / 1000
  97. return Date(timeIntervalSince1970: epoch)
  98. }
  99. }
  100. public extension Bundle {
  101. var appGroupSuiteName: String? {
  102. object(forInfoDictionaryKey: "AppGroupID") as? String
  103. }
  104. }