AppGroupSource.swift 3.5 KB

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