polscm32 aka Marvout преди 1 година
родител
ревизия
cb84264c02

+ 47 - 0
FreeAPS/Sources/APS/Storage/CarbsStorage.swift

@@ -3,17 +3,26 @@ import Foundation
 import SwiftDate
 import Swinject
 
+protocol CarbsStoredDelegate: AnyObject {
+    /*
+     Informs the delegate that the Carbs Storage has updated Carbs
+     */
+    func carbsStorageHasUpdatedCarbs(_ carbsStorage: BaseCarbsStorage)
+}
+
 protocol CarbsObserver {
     func carbsDidUpdate(_ carbs: [CarbsEntry])
 }
 
 protocol CarbsStorage {
+    var delegate: CarbsStoredDelegate? { get set }
     func storeCarbs(_ carbs: [CarbsEntry], areFetchedFromRemote: Bool) async
     func syncDate() -> Date
     func recent() -> [CarbsEntry]
     func getCarbsNotYetUploadedToNightscout() async -> [NightscoutTreatment]
     func getFPUsNotYetUploadedToNightscout() async -> [NightscoutTreatment]
     func deleteCarbs(at uniqueID: String, fpuID: String, complex: Bool)
+    func getCarbsNotYetUploadedToHealth() async -> [CarbsEntry]
 }
 
 final class BaseCarbsStorage: CarbsStorage, Injectable {
@@ -24,6 +33,8 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
 
     let coredataContext = CoreDataStack.shared.newTaskContext()
 
+    public weak var delegate: CarbsStoredDelegate?
+
     init(resolver: Resolver) {
         injectServices(resolver)
     }
@@ -37,6 +48,10 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
 
         await saveCarbEquivalents(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)
         await saveCarbsToCoreData(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)
+
+        // TODO: - Should we really use a delegate here? If yes, should we also use this for NS/TP?
+
+        delegate?.carbsStorageHasUpdatedCarbs(self)
     }
 
     private func filterRemoteEntries(entries: [CarbsEntry]) async -> [CarbsEntry] {
@@ -193,6 +208,7 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             newItem.id = UUID()
             newItem.isFPU = false
             newItem.isUploadedToNS = areFetchedFromRemote ? true : false
+            newItem.isUploadedToHealth = false
 
             do {
                 guard self.coredataContext.hasChanges else { return }
@@ -346,4 +362,35 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             }
         }
     }
+
+    func getCarbsNotYetUploadedToHealth() async -> [CarbsEntry] {
+        let results = await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: CarbEntryStored.self,
+            onContext: coredataContext,
+            predicate: NSPredicate.carbsNotYetUploadedToNightscout,
+            key: "date",
+            ascending: false
+        )
+
+        guard let carbEntries = results as? [CarbEntryStored] else {
+            return []
+        }
+
+        return await coredataContext.perform {
+            return carbEntries.map { result in
+                CarbsEntry(
+                    id: result.id?.uuidString,
+                    createdAt: result.date ?? Date(),
+                    actualDate: result.date,
+                    carbs: Decimal(result.carbs),
+                    fat: Decimal(result.fat),
+                    protein: Decimal(result.protein),
+                    note: result.note,
+                    enteredBy: "Trio",
+                    isFPU: result.isFPU,
+                    fpuID: result.fpuID?.uuidString
+                )
+            }
+        }
+    }
 }

+ 1 - 2
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -381,8 +381,7 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                     noise: nil,
                     glucose: Int(result.glucose)
                 )
-                
-                
+
 //                NightscoutTreatment(
 //                    duration: nil,
 //                    rawDuration: nil,

+ 2 - 3
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -324,12 +324,11 @@ extension NightscoutConfig {
             let glucose = await nightscoutManager.fetchGlucose(since: Date().addingTimeInterval(-1.days.timeInterval))
 
             if glucose.isNotEmpty {
-                
                 await MainActor.run {
                     self.backfilling = false
                 }
-                
-                self.glucoseStorage.storeGlucose(glucose)
+
+                glucoseStorage.storeGlucose(glucose)
 
                 Task.detached {
                     await self.healthKitManager.uploadGlucose()

+ 16 - 9
FreeAPS/Sources/Services/HealthKit/HealthKitManager.swift

@@ -32,7 +32,7 @@ protocol HealthKitManager: GlucoseSource {
     func deleteInsulin(syncID: String)
 }
 
-final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, PumpHistoryObserver {
+final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, PumpHistoryObserver, CarbsStoredDelegate {
     private enum Config {
         // unwraped HKObjects
         static var readPermissions: Set<HKSampleType> {
@@ -58,6 +58,13 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, P
 
     private var backgroundContext = CoreDataStack.shared.newTaskContext()
 
+    func carbsStorageHasUpdatedCarbs(_: BaseCarbsStorage) {
+        Task.detached { [weak self] in
+            guard let self = self else { return }
+            await self.uploadCarbs()
+        }
+    }
+
     private let processQueue = DispatchQueue(label: "BaseHealthKitManager.processQueue")
     private var lifetime = Lifetime()
 
@@ -118,6 +125,8 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, P
         broadcaster.register(CarbsObserver.self, observer: self)
         broadcaster.register(PumpHistoryObserver.self, observer: self)
 
+        carbsStorage.delegate = self
+
         debug(.service, "HealthKitManager did create")
     }
 
@@ -147,7 +156,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, P
             }
         }
     }
-    
+
     // Glucose Upload
 
     func uploadGlucose() async {
@@ -219,14 +228,13 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, P
             }
         }
     }
-    
+
     // Carbs Upload
-    
+
     func uploadCarbs() async {
-        await uploadGlucose(glucoseStorage.getGlucoseNotYetUploadedToHealth())
-        await uploadGlucose(glucoseStorage.getManualGlucoseNotYetUploadedToHealth())
+        await uploadCarbs(carbsStorage.getCarbsNotYetUploadedToHealth())
     }
-    
+
     func uploadCarbs(_ carbs: [CarbsEntry]) async {
         guard settingsManager.settings.useAppleHealth,
               let sampleType = Config.healthCarbObject,
@@ -270,7 +278,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, P
             debug(.service, "Failed to upload carb samples to HealthKit: \(error.localizedDescription)")
         }
     }
-    
+
     private func updateCarbsAsUploaded(_ carbs: [CarbsEntry]) async {
         await backgroundContext.perform {
             let ids = carbs.map(\.id) as NSArray
@@ -293,7 +301,6 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, P
         }
     }
 
-
     func saveIfNeeded(carbs: [CarbsEntry]) {
         guard settingsManager.settings.useAppleHealth,
               let sampleType = Config.healthCarbObject,

+ 1 - 0
Model/Classes+Properties/CarbEntryStored+CoreDataProperties.swift

@@ -13,6 +13,7 @@ public extension CarbEntryStored {
     @NSManaged var id: UUID?
     @NSManaged var isFPU: Bool
     @NSManaged var isUploadedToNS: Bool
+    @NSManaged var isUploadedToHealth: Bool
     @NSManaged var note: String?
     @NSManaged var protein: Double
 }

+ 1 - 0
Model/TrioCoreDataPersistentContainer.xcdatamodeld/TrioCoreDataPersistentContainer.xcdatamodel/contents

@@ -13,6 +13,7 @@
         <attribute name="fpuID" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
         <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
         <attribute name="isFPU" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="isUploadedToHealth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="isUploadedToNS" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="note" optional="YES" attributeType="String"/>
         <attribute name="protein" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>