Skip to content

Commit

Permalink
Fix healthkit issue for Insulin treatment
Browse files Browse the repository at this point in the history
Fix the healthKit sync when stopped before the end nightscout#231
Fix the 0U basal in healthKit nightscout#149
  • Loading branch information
avouspierre committed Oct 15, 2023
1 parent e56875d commit cc158e2
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 8 deletions.
5 changes: 0 additions & 5 deletions FreeAPS/Sources/APS/APSManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ final class BaseAPSManager: APSManager, Injectable {
@Injected() private var nightscout: NightscoutManager!
@Injected() private var settingsManager: SettingsManager!
@Injected() private var broadcaster: Broadcaster!
@Injected() private var healthKitManager: HealthKitManager!
@Persisted(key: "lastAutotuneDate") private var lastAutotuneDate = Date()
@Persisted(key: "lastStartLoopDate") private var lastStartLoopDate: Date = .distantPast
@Persisted(key: "lastLoopDate") var lastLoopDate: Date = .distantPast {
Expand Down Expand Up @@ -268,10 +267,6 @@ final class BaseAPSManager: APSManager, Injectable {
private func loopCompleted(error: Error? = nil, loopStatRecord: LoopStats) {
isLooping.send(false)

// save AH events
let events = pumpHistoryStorage.recent()
healthKitManager.saveIfNeeded(pumpEvents: events)

if let error = error {
warning(.apsManager, "Loop failed with error: \(error.localizedDescription)")
if let backgroundTask = backGroundTaskID {
Expand Down
47 changes: 44 additions & 3 deletions FreeAPS/Sources/Services/HealthKit/HealthKitManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protocol HealthKitManager: GlucoseSource {
func deleteInsulin(syncID: String)
}

final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, PumpHistoryObserver {
private enum Config {
// unwraped HKObjects
static var readPermissions: Set<HKSampleType> {
Expand Down Expand Up @@ -68,7 +68,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
}
get {
guard let data = persistedBGAnchor else { return nil }
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? HKQueryAnchor
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: data)
}
}

Expand Down Expand Up @@ -115,6 +115,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
enableBackgroundDelivery()

broadcaster.register(CarbsObserver.self, observer: self)
broadcaster.register(PumpHistoryObserver.self, observer: self)

debug(.service, "HealthKitManager did create")
}
Expand Down Expand Up @@ -226,6 +227,21 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
events.isNotEmpty
else { return }

func delete(syncIds: [String]?) {
syncIds?.forEach { syncID in
let predicate = HKQuery.predicateForObjects(
withMetadataKey: HKMetadataKeySyncIdentifier,
operatorType: .equalTo,
value: syncID
)

self.healthKitStore.deleteObjects(of: sampleType, predicate: predicate) { _, _, error in
guard let error = error else { return }
warning(.service, "Cannot delete sample with syncID: \(syncID)", error: error)
}
}
}

func save(bolus: [InsulinBolus], basal: [InsulinBasal]) {
let bolusSamples = bolus
.map {
Expand Down Expand Up @@ -264,6 +280,26 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
healthKitStore.save(bolusSamples + basalSamples) { _, _ in }
}

// delete existing event in HK where the amount is not the last value in the pumphistory
loadSamplesFromHealth(sampleType: sampleType, withIDs: events.map(\.id))
.receive(on: processQueue)
.compactMap { samples -> [String] in
let sampleIDs = samples.compactMap(\.syncIdentifier)
let bolusToDelete = events
.filter { $0.type == .bolus && sampleIDs.contains($0.id) }
.compactMap { event -> String? in
guard let amount = event.amount else { return nil }
guard let sampleAmount = samples.first(where: { $0.syncIdentifier == event.id }) as? HKQuantitySample
else { return nil }
if Double(amount) != sampleAmount.quantity.doubleValue(for: .internationalUnit()) {
return sampleAmount.syncIdentifier
} else { return nil }
}
return bolusToDelete
}
.sink(receiveValue: delete)
.store(in: &lifetime)

loadSamplesFromHealth(sampleType: sampleType, withIDs: events.map(\.id))
.receive(on: processQueue)
.compactMap { samples -> ([InsulinBolus], [InsulinBasal]) in
Expand All @@ -276,6 +312,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
}
let basalEvents = events
.filter { $0.type == .tempBasal && !sampleIDs.contains($0.id) }
.sorted(by: { $0.timestamp < $1.timestamp })
let basal = basalEvents.enumerated()
.compactMap { item -> InsulinBasal? in
let nextElementEventIndex = item.offset + 1
Expand All @@ -300,7 +337,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
}

let id = String(item.element.id.dropFirst())
guard amountRounded >= 0,
guard amountRounded > 0,
id != ""
else { return nil }

Expand All @@ -317,6 +354,10 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
.store(in: &lifetime)
}

func pumpHistoryDidUpdate(_ events: [PumpHistoryEvent]) {
saveIfNeeded(pumpEvents: events)
}

func createBGObserver() {
guard settingsManager.settings.useAppleHealth else { return }

Expand Down

0 comments on commit cc158e2

Please sign in to comment.