AppGroupSource.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import Combine
  2. import Foundation
  3. import LibreTransmitter
  4. struct AppGroupSource: GlucoseSource {
  5. var glucoseManager: FetchGlucoseManager?
  6. let from: String
  7. func fetch(_ heartbeat: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
  8. guard let suiteName = Bundle.main.appGroupSuiteName,
  9. let sharedDefaults = UserDefaults(suiteName: suiteName)
  10. else {
  11. return Just([]).eraseToAnyPublisher()
  12. }
  13. return Just(fetchLastBGs(60, sharedDefaults, heartbeat)).eraseToAnyPublisher()
  14. }
  15. func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
  16. fetch(nil)
  17. }
  18. private func fetchLastBGs(_ count: Int, _ sharedDefaults: UserDefaults, _ heartbeat: DispatchTimer?) -> [BloodGlucose] {
  19. guard let sharedData = sharedDefaults.data(forKey: "latestReadings") else {
  20. return []
  21. }
  22. HeartBeatManager.shared.checkCGMBluetoothTransmitter(sharedUserDefaults: sharedDefaults, heartbeat: heartbeat)
  23. debug(.deviceManager, "APPGROUP : START FETCH LAST BG ")
  24. let decoded = try? JSONSerialization.jsonObject(with: sharedData, options: [])
  25. guard let sgvs = decoded as? [AnyObject] else {
  26. return []
  27. }
  28. var results: [BloodGlucose] = []
  29. for sgv in sgvs.prefix(count) {
  30. guard
  31. let glucose = sgv["Value"] as? Int,
  32. let timestamp = sgv["DT"] as? String,
  33. let date = parseDate(timestamp)
  34. else { continue }
  35. var direction: String?
  36. // Dexcom changed the format of trend in 2021 so we accept both String/Int types
  37. if let directionString = sgv["direction"] as? String {
  38. direction = directionString
  39. } else if let intTrend = sgv["trend"] as? Int {
  40. direction = GlucoseTrend(rawValue: intTrend)?.direction
  41. } else if let intTrend = sgv["Trend"] as? Int {
  42. direction = GlucoseTrend(rawValue: intTrend)?.direction
  43. } else if let stringTrend = sgv["trend"] as? String, let intTrend = Int(stringTrend) {
  44. direction = GlucoseTrend(rawValue: intTrend)?.direction
  45. }
  46. guard let direction = direction else { continue }
  47. if let from = sgv["from"] as? String {
  48. guard from == self.from else { continue }
  49. }
  50. results.append(
  51. BloodGlucose(
  52. sgv: glucose,
  53. direction: BloodGlucose.Direction(rawValue: direction),
  54. date: Decimal(Int(date.timeIntervalSince1970 * 1000)),
  55. dateString: date,
  56. unfiltered: nil,
  57. filtered: nil,
  58. noise: nil,
  59. glucose: glucose,
  60. type: "sgv"
  61. )
  62. )
  63. }
  64. return results
  65. }
  66. private func parseDate(_ timestamp: String) -> Date? {
  67. // timestamp looks like "/Date(1462404576000)/"
  68. guard let re = try? NSRegularExpression(pattern: "\\((.*)\\)"),
  69. let match = re.firstMatch(in: timestamp, range: NSMakeRange(0, timestamp.count))
  70. else {
  71. return nil
  72. }
  73. let matchRange = match.range(at: 1)
  74. let epoch = Double((timestamp as NSString).substring(with: matchRange))! / 1000
  75. return Date(timeIntervalSince1970: epoch)
  76. }
  77. func sourceInfo() -> [String: Any]? {
  78. [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? "Not set"))"]
  79. }
  80. }
  81. public extension Bundle {
  82. var appGroupSuiteName: String? {
  83. object(forInfoDictionaryKey: "AppGroupID") as? String
  84. }
  85. }