| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- //
- // CachedGlucoseObject+CoreDataClass.swift
- // LoopKit
- //
- // Copyright © 2018 LoopKit Authors. All rights reserved.
- //
- //
- import Foundation
- import CoreData
- import HealthKit
- class CachedGlucoseObject: NSManagedObject {
- var syncVersion: Int? {
- get {
- willAccessValue(forKey: "syncVersion")
- defer { didAccessValue(forKey: "syncVersion") }
- return primitiveSyncVersion?.intValue
- }
- set {
- willChangeValue(forKey: "syncVersion")
- defer { didChangeValue(forKey: "syncVersion") }
- primitiveSyncVersion = newValue.map { NSNumber(value: $0) }
- }
- }
-
- var device: HKDevice? {
- get {
- willAccessValue(forKey: "device")
- defer { didAccessValue(forKey: "device") }
- return primitiveDevice.flatMap { try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKDevice.self, from: $0) }
- }
- set {
- willChangeValue(forKey: "device")
- defer { didChangeValue(forKey: "device") }
- primitiveDevice = newValue.flatMap { try? NSKeyedArchiver.archivedData(withRootObject: $0, requiringSecureCoding: false) }
- }
- }
-
- var condition: GlucoseCondition? {
- get {
- willAccessValue(forKey: "condition")
- defer { didAccessValue(forKey: "condition") }
- return primitiveCondition.flatMap { GlucoseCondition(rawValue: $0) }
- }
- set {
- willChangeValue(forKey: "condition")
- defer { didChangeValue(forKey: "condition") }
- primitiveCondition = newValue.map { $0.rawValue }
- }
- }
- var trend: GlucoseTrend? {
- get {
- willAccessValue(forKey: "trend")
- defer { didAccessValue(forKey: "trend") }
- return primitiveTrend.flatMap { GlucoseTrend(rawValue: $0.intValue) }
- }
- set {
- willChangeValue(forKey: "trend")
- defer { didChangeValue(forKey: "trend") }
- primitiveTrend = newValue.map { NSNumber(value: $0.rawValue) }
- }
- }
- var hasUpdatedModificationCounter: Bool { changedValues().keys.contains("modificationCounter") }
- func updateModificationCounter() { setPrimitiveValue(managedObjectContext!.modificationCounter!, forKey: "modificationCounter") }
- override func awakeFromInsert() {
- super.awakeFromInsert()
- updateModificationCounter()
- }
- override func willSave() {
- if isUpdated && !hasUpdatedModificationCounter {
- updateModificationCounter()
- }
- super.willSave()
- }
- }
- // MARK: - Helpers
- extension CachedGlucoseObject {
- var quantity: HKQuantity { HKQuantity(unit: HKUnit(from: unitString), doubleValue: value) }
- var quantitySample: HKQuantitySample {
- var metadata: [String: Any] = [:]
- metadata[HKMetadataKeySyncIdentifier] = syncIdentifier
- metadata[HKMetadataKeySyncVersion] = syncVersion
- if isDisplayOnly {
- metadata[MetadataKeyGlucoseIsDisplayOnly] = true
- }
- if wasUserEntered {
- metadata[HKMetadataKeyWasUserEntered] = true
- }
- metadata[MetadataKeyGlucoseCondition] = condition?.rawValue
- metadata[MetadataKeyGlucoseTrend] = trend?.symbol
- metadata[MetadataKeyGlucoseTrendRateUnit] = trendRateUnit
- metadata[MetadataKeyGlucoseTrendRateValue] = trendRateValue
- return HKQuantitySample(
- type: HKQuantityType.quantityType(forIdentifier: .bloodGlucose)!,
- quantity: quantity,
- start: startDate,
- end: startDate,
- device: device,
- metadata: metadata
- )
- }
- var trendRate: HKQuantity? {
- get {
- guard let trendRateUnit = trendRateUnit, let trendRateValue = trendRateValue else {
- return nil
- }
- return HKQuantity(unit: HKUnit(from: trendRateUnit), doubleValue: trendRateValue.doubleValue)
- }
- set {
- if let newValue = newValue {
- let unit = HKUnit(from: unitString).unitDivided(by: .minute())
- trendRateUnit = unit.unitString
- trendRateValue = NSNumber(value: newValue.doubleValue(for: unit))
- } else {
- trendRateUnit = nil
- trendRateValue = nil
- }
- }
- }
- }
- // MARK: - Operations
- extension CachedGlucoseObject {
- /// Creates (initializes) a `CachedGlucoseObject` from a new CGM sample from Loop.
- /// - parameters:
- /// - sample: A new glucose (CGM) sample to copy data from.
- /// - provenanceIdentifier: A string uniquely identifying the provenance (origin) of the sample.
- /// - healthKitStorageDelay: The amount of time (seconds) to delay writing this sample to HealthKit. A `nil` here means this sample is not eligible (i.e. authorized) to be written to HealthKit.
- func create(from sample: NewGlucoseSample, provenanceIdentifier: String, healthKitStorageDelay: TimeInterval?) {
- self.uuid = nil
- self.provenanceIdentifier = provenanceIdentifier
- self.syncIdentifier = sample.syncIdentifier
- self.syncVersion = sample.syncVersion
- self.value = sample.quantity.doubleValue(for: .milligramsPerDeciliter)
- self.unitString = HKUnit.milligramsPerDeciliter.unitString
- self.startDate = sample.date
- self.isDisplayOnly = sample.isDisplayOnly
- self.wasUserEntered = sample.wasUserEntered
- self.device = sample.device
- self.condition = sample.condition
- self.trend = sample.trend
- self.trendRate = sample.trendRate
- self.healthKitEligibleDate = healthKitStorageDelay.map { sample.date.addingTimeInterval($0) }
- }
- // HealthKit
- func create(from sample: HKQuantitySample) {
- self.uuid = sample.uuid
- self.provenanceIdentifier = sample.provenanceIdentifier
- self.syncIdentifier = sample.syncIdentifier
- self.syncVersion = sample.syncVersion
- self.value = sample.quantity.doubleValue(for: .milligramsPerDeciliter)
- self.unitString = HKUnit.milligramsPerDeciliter.unitString
- self.startDate = sample.startDate
- self.isDisplayOnly = sample.isDisplayOnly
- self.wasUserEntered = sample.wasUserEntered
- self.device = sample.device
- self.condition = sample.condition
- self.trend = sample.trend
- self.trendRate = sample.trendRate
- // The assumption here is that if this is created from a HKQuantitySample, it is coming out of HealthKit, and
- // therefore does not need to be written to HealthKit.
- self.healthKitEligibleDate = nil
- }
- }
- // MARK: - Watch Synchronization
- extension CachedGlucoseObject {
- func update(from sample: StoredGlucoseSample) {
- self.uuid = sample.uuid
- self.provenanceIdentifier = sample.provenanceIdentifier
- self.syncIdentifier = sample.syncIdentifier
- self.syncVersion = sample.syncVersion
- self.value = sample.quantity.doubleValue(for: .milligramsPerDeciliter)
- self.unitString = HKUnit.milligramsPerDeciliter.unitString
- self.startDate = sample.startDate
- self.isDisplayOnly = sample.isDisplayOnly
- self.wasUserEntered = sample.wasUserEntered
- self.device = sample.device
- self.condition = sample.condition
- self.trend = sample.trend
- self.trendRate = sample.trendRate
- self.healthKitEligibleDate = sample.healthKitEligibleDate
- }
- }
|