| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- import Combine
- import Foundation
- import SwiftDate
- import Swinject
- protocol FetchGlucoseManager {}
- final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
- private let processQueue = DispatchQueue(label: "BaseGlucoseManager.processQueue")
- @Injected() var glucoseStorage: GlucoseStorage!
- @Injected() var nightscoutManager: NightscoutManager!
- @Injected() var apsManager: APSManager!
- private var lifetime = Set<AnyCancellable>()
- private let timer = DispatchTimer(timeInterval: 1.minutes.timeInterval)
- init(resolver: Resolver) {
- injectServices(resolver)
- subscribe()
- }
- private func subscribe() {
- timer.publisher
- .receive(on: processQueue)
- .flatMap { date -> AnyPublisher<(Date, Date, [BloodGlucose]), Never> in
- debug(.nightscout, "FetchGlucoseManager heartbeat")
- debug(.nightscout, "Start fetching glucose")
- return Publishers.CombineLatest3(
- Just(date),
- Just(self.glucoseStorage.syncDate()),
- Publishers.CombineLatest(
- self.nightscoutManager.fetchGlucose(),
- self.fetchGlucoseFromSharedGroup()
- )
- .map { [$0, $1].flatMap { $0 } }
- .eraseToAnyPublisher()
- )
- .eraseToAnyPublisher()
- }
- .sink { date, syncDate, glucose in
- // Because of Spike dosn't respect a date query
- let filteredByDate = glucose.filter { $0.dateString > syncDate }
- let filtered = self.glucoseStorage.filterTooFrequentGlucose(filteredByDate, at: syncDate)
- if !filtered.isEmpty {
- debug(.nightscout, "New glucose found")
- self.glucoseStorage.storeGlucose(filtered)
- self.apsManager.heartbeat(date: date, force: true)
- }
- }
- .store(in: &lifetime)
- timer.resume()
- }
- private func fetchGlucoseFromSharedGroup() -> AnyPublisher<[BloodGlucose], Never> {
- guard let suiteName = Bundle.main.appGroupSuiteName,
- let sharedDefaults = UserDefaults(suiteName: suiteName)
- else {
- return Just([]).eraseToAnyPublisher()
- }
- return Just(fetchLastBGs(60, sharedDefaults)).eraseToAnyPublisher()
- }
- private func fetchLastBGs(_ count: Int, _ sharedDefaults: UserDefaults) -> [BloodGlucose] {
- guard let sharedData = sharedDefaults.data(forKey: "latestReadings") else {
- return []
- }
- let decoded = try? JSONSerialization.jsonObject(with: sharedData, options: [])
- guard let sgvs = decoded as? [AnyObject] else {
- return []
- }
- var results: [BloodGlucose] = []
- for sgv in sgvs.prefix(count) {
- guard
- let glucose = sgv["Value"] as? Int,
- let direction = sgv["direction"] as? String,
- let timestamp = sgv["DT"] as? String,
- let date = parseDate(timestamp)
- else { continue }
- results.append(
- BloodGlucose(
- _id: UUID().uuidString,
- sgv: glucose,
- direction: BloodGlucose.Direction(rawValue: direction),
- date: Decimal(Int(date.timeIntervalSince1970 * 1000)),
- dateString: date,
- filtered: nil,
- noise: nil,
- glucose: glucose
- )
- )
- }
- return results
- }
- private func parseDate(_ timestamp: String) -> Date? {
- // timestamp looks like "/Date(1462404576000)/"
- guard let re = try? NSRegularExpression(pattern: "\\((.*)\\)"),
- let match = re.firstMatch(in: timestamp, range: NSMakeRange(0, timestamp.count))
- else {
- return nil
- }
- let matchRange = match.range(at: 1)
- let epoch = Double((timestamp as NSString).substring(with: matchRange))! / 1000
- return Date(timeIntervalSince1970: epoch)
- }
- }
- public extension Bundle {
- var appGroupSuiteName: String? {
- object(forInfoDictionaryKey: "AppGroupID") as? String
- }
- }
|