| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- //
- // CGMManager.swift
- // Loop
- //
- // Copyright © 2017 LoopKit Authors. All rights reserved.
- //
- import HealthKit
- /// Describes the result of CGM manager operations to fetch and report sensor readings.
- ///
- /// - noData: No new data was available or retrieved
- /// - unreliableData: New glucose data was received, but is not reliable enough to use for therapy
- /// - newData: New glucose data was received and stored
- /// - error: An error occurred while receiving or store data
- public enum CGMReadingResult {
- case noData
- case unreliableData
- case newData([NewGlucoseSample])
- case error(Error)
- }
- public struct CGMManagerStatus: Equatable {
- // Return false if no sensor active, or in a state where no future data is expected without user intervention
- public var hasValidSensorSession: Bool
- public var lastCommunicationDate: Date?
- public var device: HKDevice?
-
- public init(hasValidSensorSession: Bool, lastCommunicationDate: Date? = nil, device: HKDevice?) {
- self.hasValidSensorSession = hasValidSensorSession
- self.lastCommunicationDate = lastCommunicationDate
- self.device = device
- }
- }
- extension CGMManagerStatus: Codable {
- public init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- self.hasValidSensorSession = try container.decode(Bool.self, forKey: .hasValidSensorSession)
- self.lastCommunicationDate = try container.decodeIfPresent(Date.self, forKey: .lastCommunicationDate)
- self.device = try container.decodeIfPresent(CodableDevice.self, forKey: .device)?.device
- }
- public func encode(to encoder: Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(hasValidSensorSession, forKey: .hasValidSensorSession)
- try container.encodeIfPresent(lastCommunicationDate, forKey: .lastCommunicationDate)
- try container.encodeIfPresent(device.map { CodableDevice($0) }, forKey: .device)
- }
- private enum CodingKeys: String, CodingKey {
- case hasValidSensorSession
- case lastCommunicationDate
- case device
- }
- }
- public protocol CGMManagerStatusObserver: AnyObject {
- /// Notifies observers of changes in CGMManagerStatus
- ///
- /// - Parameter manager: The manager instance
- /// - Parameter status: The new, updated status. Status includes properties associated with the manager, transmitter, or sensor,
- /// that are not part of an individual sensor reading.
- func cgmManager(_ manager: CGMManager, didUpdate status: CGMManagerStatus)
- }
- public protocol CGMManagerDelegate: DeviceManagerDelegate, CGMManagerStatusObserver {
- /// Asks the delegate for a date with which to filter incoming glucose data
- ///
- /// - Parameter manager: The manager instance
- /// - Returns: The date data occuring on or after which should be kept
- func startDateToFilterNewData(for manager: CGMManager) -> Date?
- /// Informs the delegate that the device has updated with a new result
- ///
- /// - Parameters:
- /// - manager: The manager instance
- /// - result: The result of the update
- func cgmManager(_ manager: CGMManager, hasNew readingResult: CGMReadingResult) -> Void
- /// Informs the delegate that the manager is deactivating and should be deleted
- ///
- /// - Parameter manager: The manager instance
- func cgmManagerWantsDeletion(_ manager: CGMManager)
- /// Informs the delegate that the manager has updated its state and should be persisted.
- ///
- /// - Parameter manager: The manager instance
- func cgmManagerDidUpdateState(_ manager: CGMManager)
-
- /// Asks the delegate for credential store prefix to avoid namespace conflicts
- ///
- /// - Parameter manager: The manager instance
- /// - Returns: The unique prefix for the credential store
- func credentialStoragePrefix(for manager: CGMManager) -> String
- }
- public protocol CGMManager: DeviceManager {
- var cgmManagerDelegate: CGMManagerDelegate? { get set }
- var appURL: URL? { get }
- /// Whether the device is capable of waking the app
- var providesBLEHeartbeat: Bool { get }
- /// The length of time to keep samples in HealthKit before removal. Return nil to never remove samples.
- var managedDataInterval: TimeInterval? { get }
-
- /// The length of time to delay until storing samples into HealthKit. Return 0 for no delay.
- static var healthKitStorageDelay: TimeInterval { get }
- var shouldSyncToRemoteService: Bool { get }
- var glucoseDisplay: GlucoseDisplayable? { get }
- /// The current status of the cgm manager
- var cgmManagerStatus: CGMManagerStatus { get }
- /// The queue on which CGMManagerDelegate methods are called
- /// Setting to nil resets to a default provided by the manager
- var delegateQueue: DispatchQueue! { get set }
- /// Implementations of this function must call the `completion` block, with the appropriate `CGMReadingResult`
- /// according to the current available data.
- /// - If there is new unreliable data, return `.unreliableData`
- /// - If there is no new data and the current data is unreliable, return `.unreliableData`
- /// - If there is new reliable data, return `.newData` with the data samples
- /// - If there is no new data and the current data is reliable, return `.noData`
- /// - If there is an error, return `.error` with the appropriate error.
- ///
- /// - Parameters:
- /// - completion: A closure called when operation has completed
- func fetchNewDataIfNeeded(_ completion: @escaping (CGMReadingResult) -> Void) -> Void
- /// Adds an observer of changes in CGMManagerStatus
- ///
- /// Observers are held by weak reference.
- ///
- /// - Parameters:
- /// - observer: The observing object
- /// - queue: The queue on which the observer methods should be called
- func addStatusObserver(_ observer: CGMManagerStatusObserver, queue: DispatchQueue)
- /// Removes an observer of changes in CGMManagerStatus
- ///
- /// Since observers are held weakly, calling this method is not required when the observer is deallocated
- ///
- /// - Parameter observer: The observing object
- func removeStatusObserver(_ observer: CGMManagerStatusObserver)
- /// Requests that the manager completes its deletion process
- ///
- /// - Parameter completion: Action to take after the CGM manager is deleted
- func delete(completion: @escaping () -> Void)
- }
- public extension CGMManager {
- var appURL: URL? {
- return nil
- }
-
- /// Default is no delay to store samples in HealthKit
- static var healthKitStorageDelay: TimeInterval { 0 }
- /// Convenience wrapper for notifying the delegate of deletion on the delegate queue
- ///
- /// - Parameters:
- /// - completion: A closure called from the delegate queue after the delegate is called
- func notifyDelegateOfDeletion(completion: @escaping () -> Void) {
- delegateQueue.async {
- self.cgmManagerDelegate?.cgmManagerWantsDeletion(self)
- completion()
- }
- }
-
- func addStatusObserver(_ observer: CGMManagerStatusObserver, queue: DispatchQueue) {
- // optional since a CGM manager may not support status observers
- }
- func removeStatusObserver(_ observer: CGMManagerStatusObserver) {
- // optional since a CGM manager may not support status observers
- }
- /// Override this default behaviour if the CGM Manager needs to complete tasks before being deleted
- func delete(completion: @escaping () -> Void) {
- notifyDelegateOfDeletion(completion: completion)
- }
- }
|