Просмотр исходного кода

Fix threading issue in TP Manager, Fix for Health Upload WiP (not yet working)

polscm32 1 год назад
Родитель
Сommit
4da52d489e

+ 13 - 6
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -467,12 +467,19 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                         amount: event.bolus?.amount as Decimal?
                         amount: event.bolus?.amount as Decimal?
                     )
                     )
                 case PumpEvent.tempBasal.rawValue:
                 case PumpEvent.tempBasal.rawValue:
-                    return PumpHistoryEvent(
-                        id: event.id ?? UUID().uuidString,
-                        type: .tempBasal,
-                        timestamp: event.timestamp ?? Date(),
-                        amount: event.tempBasal?.rate as Decimal?
-                    )
+                    if let id = event.id, let timestamp = event.timestamp, let tempBasal = event.tempBasal,
+                       let tempBasalRate = tempBasal.rate
+                    {
+                        return PumpHistoryEvent(
+                            id: id,
+                            type: .tempBasal,
+                            timestamp: timestamp,
+                            amount: tempBasalRate as Decimal,
+                            duration: Int(tempBasal.duration)
+                        )
+                    } else {
+                        return nil
+                    }
                 default:
                 default:
                     return nil
                     return nil
                 }
                 }

+ 104 - 13
FreeAPS/Sources/Services/HealthKit/HealthKitManager.swift

@@ -74,6 +74,11 @@ final class BaseHealthKitManager: HealthKitManager, Injectable {
 
 
         registerHandlers()
         registerHandlers()
 
 
+        Task { [weak self] in
+            guard let self = self else { return }
+            await self.uploadInsulin()
+        }
+
         guard isAvailableOnCurrentDevice,
         guard isAvailableOnCurrentDevice,
               AppleHealthConfig.healthBGObject != nil else { return }
               AppleHealthConfig.healthBGObject != nil else { return }
 
 
@@ -331,31 +336,69 @@ final class BaseHealthKitManager: HealthKitManager, Injectable {
               insulin.isNotEmpty
               insulin.isNotEmpty
         else { return }
         else { return }
 
 
+        // Fetch existing temp basal entries from Core Data
+        let results = await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: PumpEventStored.self,
+            onContext: backgroundContext,
+            predicate: NSCompoundPredicate(andPredicateWithSubpredicates: [
+                NSPredicate.pumpHistoryLast24h,
+                NSPredicate(format: "tempBasal != nil")
+            ]),
+            key: "timestamp",
+            ascending: true,
+            batchSize: 50
+        )
+
+        var processedEvents: [PumpHistoryEvent] = []
+
+        await backgroundContext.perform {
+            guard let existingTempBasalEntries = results as? [PumpEventStored] else { return }
+
+            for event in insulin {
+                switch event.type {
+                case .tempBasal:
+                    let tempBasalEvents = self.processTempBasalEvent(
+                        event,
+                        existingTempBasalEntries: existingTempBasalEntries
+                    )
+                    processedEvents.append(contentsOf: tempBasalEvents)
+
+                case .bolus:
+                    processedEvents.append(event)
+
+                default:
+                    break
+                }
+            }
+        }
+
+        // Create HKQuantitySamples for all processed events
         do {
         do {
-            let insulinSamples = insulin.compactMap { insulinSample -> HKQuantitySample? in
-                guard let insulinValue = insulinSample.amount else { return nil }
+            let insulinSamples = processedEvents.compactMap { processedEvent -> HKQuantitySample? in
+                guard let insulinValue = processedEvent.amount else { return nil }
 
 
-                // Determine the insulin delivery reason (bolus or basal)
                 let deliveryReason: HKInsulinDeliveryReason
                 let deliveryReason: HKInsulinDeliveryReason
-                switch insulinSample.type {
+                switch processedEvent.type {
                 case .bolus:
                 case .bolus:
                     deliveryReason = .bolus
                     deliveryReason = .bolus
                 case .tempBasal:
                 case .tempBasal:
                     deliveryReason = .basal
                     deliveryReason = .basal
                 default:
                 default:
-                    // Skip other types
-                    /// If deliveryReason is nil, the compactMap will filter this sample out preventing a crash
                     return nil
                     return nil
                 }
                 }
 
 
+                // adjust end date based on duration
+                let endDate = processedEvent.timestamp
+                    .addingTimeInterval(TimeInterval(minutes: Double(processedEvent.duration ?? 0)))
+
                 return HKQuantitySample(
                 return HKQuantitySample(
                     type: sampleType,
                     type: sampleType,
                     quantity: HKQuantity(unit: .internationalUnit(), doubleValue: Double(insulinValue)),
                     quantity: HKQuantity(unit: .internationalUnit(), doubleValue: Double(insulinValue)),
-                    start: insulinSample.timestamp,
-                    end: insulinSample.timestamp,
+                    start: processedEvent.timestamp,
+                    end: endDate,
                     metadata: [
                     metadata: [
-                        HKMetadataKeyExternalUUID: insulinSample.id,
-                        HKMetadataKeySyncIdentifier: insulinSample.id,
+                        HKMetadataKeyExternalUUID: processedEvent.id,
+                        HKMetadataKeySyncIdentifier: processedEvent.id,
                         HKMetadataKeySyncVersion: 1,
                         HKMetadataKeySyncVersion: 1,
                         HKMetadataKeyInsulinDeliveryReason: deliveryReason.rawValue,
                         HKMetadataKeyInsulinDeliveryReason: deliveryReason.rawValue,
                         AppleHealthConfig.TrioMetaDataKey: UUID().uuidString
                         AppleHealthConfig.TrioMetaDataKey: UUID().uuidString
@@ -368,18 +411,66 @@ final class BaseHealthKitManager: HealthKitManager, Injectable {
                 return
                 return
             }
             }
 
 
-            // Attempt to save the insulin samples to Apple Health
             try await healthKitStore.save(insulinSamples)
             try await healthKitStore.save(insulinSamples)
             debug(.service, "Successfully stored \(insulinSamples.count) insulin samples in HealthKit.")
             debug(.service, "Successfully stored \(insulinSamples.count) insulin samples in HealthKit.")
 
 
-            // After successful upload, update the isUploadedToHealth flag in Core Data
-            await updateInsulinAsUploaded(insulin)
+            await updateInsulinAsUploaded(processedEvents)
 
 
         } catch {
         } catch {
             debug(.service, "Failed to upload insulin samples to HealthKit: \(error.localizedDescription)")
             debug(.service, "Failed to upload insulin samples to HealthKit: \(error.localizedDescription)")
         }
         }
     }
     }
 
 
+    private func processTempBasalEvent(
+        _ event: PumpHistoryEvent,
+        existingTempBasalEntries: [PumpEventStored]
+    ) -> [PumpHistoryEvent] {
+        var processedTempBasalEvents: [PumpHistoryEvent] = []
+
+        backgroundContext.performAndWait {
+            guard let duration = event.duration, let amount = event.amount else { return }
+            let value = (Decimal(duration) / 60.0) * amount
+
+            if let matchingEntryIndex = existingTempBasalEntries.firstIndex(where: { $0.timestamp == event.timestamp }) {
+                let predecessorIndex = matchingEntryIndex - 1
+                if predecessorIndex >= 0 {
+                    let predecessorEntry = existingTempBasalEntries[predecessorIndex]
+
+                    if let predecessorTimestamp = predecessorEntry.timestamp {
+                        let predecessorEndDate = predecessorTimestamp
+                            .addingTimeInterval(TimeInterval(Int(predecessorEntry.tempBasal?.duration ?? 0) * 60))
+                        if predecessorEndDate > event.timestamp {
+                            let adjustedEndDate = event.timestamp
+                            let adjustedDuration = adjustedEndDate.timeIntervalSince(predecessorTimestamp)
+
+                            let adjustedPumpHistoryEvent = PumpHistoryEvent(
+                                id: UUID().uuidString,
+                                type: .tempBasal,
+                                timestamp: predecessorTimestamp,
+                                amount: Decimal(adjustedDuration / 3600) * amount,
+                                duration: Int(adjustedDuration / 60)
+                            )
+
+                            processedTempBasalEvents.append(adjustedPumpHistoryEvent)
+                        }
+                    }
+                }
+
+                let newPumpHistoryEvent = PumpHistoryEvent(
+                    id: UUID().uuidString,
+                    type: .tempBasal,
+                    timestamp: event.timestamp,
+                    amount: value,
+                    duration: event.duration
+                )
+
+                processedTempBasalEvents.append(newPumpHistoryEvent)
+            }
+        }
+
+        return processedTempBasalEvents
+    }
+
     private func updateInsulinAsUploaded(_ insulin: [PumpHistoryEvent]) async {
     private func updateInsulinAsUploaded(_ insulin: [PumpHistoryEvent]) async {
         await backgroundContext.perform {
         await backgroundContext.perform {
             let ids = insulin.map(\.id) as NSArray
             let ids = insulin.map(\.id) as NSArray

+ 182 - 175
FreeAPS/Sources/Services/Network/TidepoolManager.swift

@@ -98,32 +98,32 @@ final class BaseTidepoolManager: TidepoolManager, Injectable {
 
 
     /// Registers handlers for Core Data changes
     /// Registers handlers for Core Data changes
     private func registerHandlers() {
     private func registerHandlers() {
-            coreDataPublisher?.filterByEntityName("PumpEventStored").sink { [weak self] _ in
+        coreDataPublisher?.filterByEntityName("PumpEventStored").sink { [weak self] _ in
+            guard let self = self else { return }
+            Task { [weak self] in
                 guard let self = self else { return }
                 guard let self = self else { return }
-                Task { [weak self] in
-                    guard let self = self else { return }
-                    await self.uploadInsulin()
-                }
-            }.store(in: &subscriptions)
+                await self.uploadInsulin()
+            }
+        }.store(in: &subscriptions)
 
 
-            coreDataPublisher?.filterByEntityName("CarbEntryStored").sink { [weak self] _ in
+        coreDataPublisher?.filterByEntityName("CarbEntryStored").sink { [weak self] _ in
+            guard let self = self else { return }
+            Task { [weak self] in
                 guard let self = self else { return }
                 guard let self = self else { return }
-                Task { [weak self] in
-                    guard let self = self else { return }
-                    await self.uploadCarbs()
-                }
-            }.store(in: &subscriptions)
+                await self.uploadCarbs()
+            }
+        }.store(in: &subscriptions)
 
 
-            // TODO: this is currently done in FetchGlucoseManager and forced there inside a background task.
-            // leave it there, or move it here? not sure…
-            coreDataPublisher?.filterByEntityName("GlucoseStored").sink { [weak self] _ in
+        // TODO: this is currently done in FetchGlucoseManager and forced there inside a background task.
+        // leave it there, or move it here? not sure…
+        coreDataPublisher?.filterByEntityName("GlucoseStored").sink { [weak self] _ in
+            guard let self = self else { return }
+            Task { [weak self] in
                 guard let self = self else { return }
                 guard let self = self else { return }
-                Task { [weak self] in
-                    guard let self = self else { return }
-                    await self.uploadGlucose()
-                }
-            }.store(in: &subscriptions)
-        }
+                await self.uploadGlucose()
+            }
+        }.store(in: &subscriptions)
+    }
 
 
     private func subscribe() {
     private func subscribe() {
         broadcaster.register(TempTargetsObserver.self, observer: self)
         broadcaster.register(TempTargetsObserver.self, observer: self)
@@ -273,110 +273,112 @@ extension BaseTidepoolManager {
 /// Insulin Upload and Deletion Functionality
 /// Insulin Upload and Deletion Functionality
 extension BaseTidepoolManager {
 extension BaseTidepoolManager {
     func uploadInsulin() async {
     func uploadInsulin() async {
-        uploadDose(await pumpHistoryStorage.getPumpHistoryNotYetUploadedToTidepool())
+        await uploadDose(await pumpHistoryStorage.getPumpHistoryNotYetUploadedToTidepool())
     }
     }
 
 
-    func uploadDose(_ events: [PumpHistoryEvent]) {
+    func uploadDose(_ events: [PumpHistoryEvent]) async {
         guard !events.isEmpty, let tidepoolService = self.tidepoolService else { return }
         guard !events.isEmpty, let tidepoolService = self.tidepoolService else { return }
 
 
         // Fetch all temp basal entries from Core Data for the last 24 hours
         // Fetch all temp basal entries from Core Data for the last 24 hours
-        let existingTempBasalEntries: [PumpEventStored] = CoreDataStack.shared.fetchEntities(
+        let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: PumpEventStored.self,
             ofType: PumpEventStored.self,
             onContext: backgroundContext,
             onContext: backgroundContext,
-            predicate: NSPredicate.pumpHistoryLast24h,
+            predicate: NSCompoundPredicate(andPredicateWithSubpredicates: [
+                NSPredicate.pumpHistoryLast24h,
+                NSPredicate(format: "tempBasal != nil")
+            ]),
             key: "timestamp",
             key: "timestamp",
             ascending: true,
             ascending: true,
             batchSize: 50
             batchSize: 50
-        ).filter { $0.tempBasal != nil }
-
-        var insulinDoseEvents: [DoseEntry] = events.reduce([]) { result, event in
-            var result = result
-            switch event.type {
-            case .tempBasal:
-                result
-                    .append(contentsOf: self.processTempBasalEvent(event, existingTempBasalEntries: existingTempBasalEntries))
-
-            case .bolus:
-                let bolusDoseEntry = DoseEntry(
-                    type: .bolus,
-                    startDate: event.timestamp,
-                    endDate: event.timestamp,
-                    value: Double(event.amount!),
-                    unit: .units,
-                    deliveredUnits: nil,
-                    syncIdentifier: event.id,
-                    scheduledBasalRate: nil,
-                    insulinType: apsManager.pumpManager?.status.insulinType ?? nil,
-                    automatic: event.isSMB ?? true,
-                    manuallyEntered: event.isExternal ?? false
-                )
-
-                result.append(bolusDoseEntry)
+        )
 
 
-            default:
-                break
+        // Ensure that the processing happens within the background context for thread safety
+        await backgroundContext.perform {
+            guard let existingTempBasalEntries = results as? [PumpEventStored] else { return }
+
+            let insulinDoseEvents: [DoseEntry] = events.reduce([]) { result, event in
+                var result = result
+                switch event.type {
+                case .tempBasal:
+                    result
+                        .append(contentsOf: self.processTempBasalEvent(event, existingTempBasalEntries: existingTempBasalEntries))
+                case .bolus:
+                    let bolusDoseEntry = DoseEntry(
+                        type: .bolus,
+                        startDate: event.timestamp,
+                        endDate: event.timestamp,
+                        value: Double(event.amount!),
+                        unit: .units,
+                        deliveredUnits: nil,
+                        syncIdentifier: event.id,
+                        scheduledBasalRate: nil,
+                        insulinType: self.apsManager.pumpManager?.status.insulinType ?? nil,
+                        automatic: event.isSMB ?? true,
+                        manuallyEntered: event.isExternal ?? false
+                    )
+                    result.append(bolusDoseEntry)
+                default:
+                    break
+                }
+                return result
             }
             }
 
 
-            return result
-        }
-
-        debug(.service, "TIDEPOOL DOSE ENTRIES: \(insulinDoseEvents)")
+            debug(.service, "TIDEPOOL DOSE ENTRIES: \(insulinDoseEvents)")
+
+            let pumpEvents: [PersistedPumpEvent] = events.compactMap { event -> PersistedPumpEvent? in
+                if let pumpEventType = event.type.mapEventTypeToPumpEventType() {
+                    let dose: DoseEntry? = switch pumpEventType {
+                    case .suspend:
+                        DoseEntry(suspendDate: event.timestamp, automatic: true)
+                    case .resume:
+                        DoseEntry(resumeDate: event.timestamp, automatic: true)
+                    default:
+                        nil
+                    }
 
 
-        let pumpEvents: [PersistedPumpEvent] = events.compactMap { event -> PersistedPumpEvent? in
-            if let pumpEventType = event.type.mapEventTypeToPumpEventType() {
-                let dose: DoseEntry? = switch pumpEventType {
-                case .suspend:
-                    DoseEntry(suspendDate: event.timestamp, automatic: true)
-                case .resume:
-                    DoseEntry(resumeDate: event.timestamp, automatic: true)
-                default:
-                    nil
+                    return PersistedPumpEvent(
+                        date: event.timestamp,
+                        persistedDate: event.timestamp,
+                        dose: dose,
+                        isUploaded: true,
+                        objectIDURL: URL(string: "x-coredata:///PumpEvent/\(event.id)")!,
+                        raw: event.id.data(using: .utf8),
+                        title: event.note,
+                        type: pumpEventType
+                    )
+                } else {
+                    return nil
                 }
                 }
-
-                return PersistedPumpEvent(
-                    date: event.timestamp,
-                    persistedDate: event.timestamp,
-                    dose: dose,
-                    isUploaded: true,
-                    objectIDURL: URL(string: "x-coredata:///PumpEvent/\(event.id)")!,
-                    raw: event.id.data(using: .utf8),
-                    title: event.note,
-                    type: pumpEventType
-                )
-            } else {
-                return nil
             }
             }
-        }
 
 
-        processQueue.async {
-            tidepoolService.uploadDoseData(created: insulinDoseEvents, deleted: []) { result in
-                switch result {
-                case let .failure(error):
-                    debug(.nightscout, "Error synchronizing dose data with Tidepool: \(String(describing: error))")
-                case .success:
-                    debug(.nightscout, "Success synchronizing dose data. Upload to Tidepool complete.")
-                    // After successful upload, update the isUploadedToTidepool flag in Core Data
-                    Task {
-                        let insulinEvents = events.filter {
-                            $0.type == .tempBasal || $0.type == .tempBasalDuration || $0.type == .bolus
+            self.processQueue.async {
+                tidepoolService.uploadDoseData(created: insulinDoseEvents, deleted: []) { result in
+                    switch result {
+                    case let .failure(error):
+                        debug(.nightscout, "Error synchronizing dose data with Tidepool: \(String(describing: error))")
+                    case .success:
+                        debug(.nightscout, "Success synchronizing dose data. Upload to Tidepool complete.")
+                        Task {
+                            let insulinEvents = events.filter {
+                                $0.type == .tempBasal || $0.type == .tempBasalDuration || $0.type == .bolus
+                            }
+                            await self.updateInsulinAsUploaded(insulinEvents)
                         }
                         }
-                        await self.updateInsulinAsUploaded(insulinEvents)
                     }
                     }
                 }
                 }
-            }
 
 
-            tidepoolService.uploadPumpEventData(pumpEvents) { result in
-                switch result {
-                case let .failure(error):
-                    debug(.nightscout, "Error synchronizing pump events data: \(String(describing: error))")
-                case .success:
-                    debug(.nightscout, "Success synchronizing pump events data. Upload to Tidepool complete.")
-                    // After successful upload, update the isUploadedToTidepool flag in Core Data
-                    Task {
-                        let pumpEventType = events.map { $0.type.mapEventTypeToPumpEventType() }
-                        let pumpEvents = events.filter { _ in pumpEventType.contains(pumpEventType) }
+                tidepoolService.uploadPumpEventData(pumpEvents) { result in
+                    switch result {
+                    case let .failure(error):
+                        debug(.nightscout, "Error synchronizing pump events data: \(String(describing: error))")
+                    case .success:
+                        debug(.nightscout, "Success synchronizing pump events data. Upload to Tidepool complete.")
+                        Task {
+                            let pumpEventType = events.map { $0.type.mapEventTypeToPumpEventType() }
+                            let pumpEvents = events.filter { _ in pumpEventType.contains(pumpEventType) }
 
 
-                        await self.updateInsulinAsUploaded(pumpEvents)
+                            await self.updateInsulinAsUploaded(pumpEvents)
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -432,83 +434,88 @@ extension BaseTidepoolManager {
 
 
 /// Insulin Helper Functions
 /// Insulin Helper Functions
 extension BaseTidepoolManager {
 extension BaseTidepoolManager {
-    private func processTempBasalEvent(_ event: PumpHistoryEvent, existingTempBasalEntries: [PumpEventStored]) -> [DoseEntry] {
+    private func processTempBasalEvent(
+        _ event: PumpHistoryEvent,
+        existingTempBasalEntries: [PumpEventStored]
+    ) -> [DoseEntry] {
         var insulinDoseEvents: [DoseEntry] = []
         var insulinDoseEvents: [DoseEntry] = []
 
 
-        // Loop through the pump history events
-        guard let duration = event.duration, let amount = event.amount,
-              let currentBasalRate = getCurrentBasalRate()
-        else {
-            return []
-        }
-        let value = (Decimal(duration) / 60.0) * amount
-
-        // Find the corresponding temp basal entry in existingTempBasalEntries
-        if let matchingEntryIndex = existingTempBasalEntries.firstIndex(where: { $0.timestamp == event.timestamp }) {
-            // Check for a predecessor (the entry before the matching entry)
-            let predecessorIndex = matchingEntryIndex - 1
-            if predecessorIndex >= 0 {
-                let predecessorEntry = existingTempBasalEntries[predecessorIndex]
-
-                if let predecessorTimestamp = predecessorEntry.timestamp,
-                   let predecessorEntrySyncIdentifier = predecessorEntry.id
-                {
-                    let predecessorEndDate = predecessorTimestamp
-                        .addingTimeInterval(TimeInterval(
-                            Int(predecessorEntry.tempBasal?.duration ?? 0) *
-                                60
-                        )) // parse duration to minutes
-
-                    // If the predecessor's end date is later than the current event's start date, adjust it
-                    if predecessorEndDate > event.timestamp {
-                        let adjustedEndDate = event.timestamp
-                        let adjustedDuration = adjustedEndDate.timeIntervalSince(predecessorTimestamp)
-                        let adjustedDeliveredUnits = (adjustedDuration / 3600) *
-                            Double(truncating: predecessorEntry.tempBasal?.rate ?? 0)
-
-                        // Create updated predecessor dose entry
-                        let updatedPredecessorEntry = DoseEntry(
-                            type: .tempBasal,
-                            startDate: predecessorTimestamp,
-                            endDate: adjustedEndDate,
-                            value: adjustedDeliveredUnits,
-                            unit: .units,
-                            deliveredUnits: adjustedDeliveredUnits,
-                            syncIdentifier: predecessorEntrySyncIdentifier,
-                            insulinType: apsManager.pumpManager?.status.insulinType ?? nil,
-                            automatic: true,
-                            manuallyEntered: false,
-                            isMutable: false
-                        )
-
-                        // Add the updated predecessor entry to the result
-                        insulinDoseEvents.append(updatedPredecessorEntry)
+        backgroundContext.performAndWait {
+            // Loop through the pump history events within the background context
+            guard let duration = event.duration, let amount = event.amount,
+                  let currentBasalRate = self.getCurrentBasalRate()
+            else {
+                return
+            }
+            let value = (Decimal(duration) / 60.0) * amount
+
+            // Find the corresponding temp basal entry in existingTempBasalEntries
+            if let matchingEntryIndex = existingTempBasalEntries.firstIndex(where: { $0.timestamp == event.timestamp }) {
+                // Check for a predecessor (the entry before the matching entry)
+                let predecessorIndex = matchingEntryIndex - 1
+                if predecessorIndex >= 0 {
+                    let predecessorEntry = existingTempBasalEntries[predecessorIndex]
+
+                    if let predecessorTimestamp = predecessorEntry.timestamp,
+                       let predecessorEntrySyncIdentifier = predecessorEntry.id
+                    {
+                        let predecessorEndDate = predecessorTimestamp
+                            .addingTimeInterval(TimeInterval(
+                                Int(predecessorEntry.tempBasal?.duration ?? 0) *
+                                    60
+                            )) // parse duration to minutes
+
+                        // If the predecessor's end date is later than the current event's start date, adjust it
+                        if predecessorEndDate > event.timestamp {
+                            let adjustedEndDate = event.timestamp
+                            let adjustedDuration = adjustedEndDate.timeIntervalSince(predecessorTimestamp)
+                            let adjustedDeliveredUnits = (adjustedDuration / 3600) *
+                                Double(truncating: predecessorEntry.tempBasal?.rate ?? 0)
+
+                            // Create updated predecessor dose entry
+                            let updatedPredecessorEntry = DoseEntry(
+                                type: .tempBasal,
+                                startDate: predecessorTimestamp,
+                                endDate: adjustedEndDate,
+                                value: adjustedDeliveredUnits,
+                                unit: .units,
+                                deliveredUnits: adjustedDeliveredUnits,
+                                syncIdentifier: predecessorEntrySyncIdentifier,
+                                insulinType: self.apsManager.pumpManager?.status.insulinType ?? nil,
+                                automatic: true,
+                                manuallyEntered: false,
+                                isMutable: false
+                            )
+
+                            // Add the updated predecessor entry to the result
+                            insulinDoseEvents.append(updatedPredecessorEntry)
+                        }
                     }
                     }
                 }
                 }
-            }
 
 
-            // Create a new dose entry for the current event
-            let currentEndDate = event.timestamp.addingTimeInterval(TimeInterval(minutes: Double(duration)))
-            let newDoseEntry = DoseEntry(
-                type: .tempBasal,
-                startDate: event.timestamp,
-                endDate: currentEndDate,
-                value: Double(value),
-                unit: .units,
-                deliveredUnits: Double(value),
-                syncIdentifier: event.id,
-                scheduledBasalRate: HKQuantity(
-                    unit: .internationalUnitsPerHour,
-                    doubleValue: Double(currentBasalRate.rate)
-                ),
-                insulinType: apsManager.pumpManager?.status.insulinType ?? nil,
-                automatic: true,
-                manuallyEntered: false,
-                isMutable: false
-            )
-
-            // Add the new event entry to the result
-            insulinDoseEvents.append(newDoseEntry)
+                // Create a new dose entry for the current event
+                let currentEndDate = event.timestamp.addingTimeInterval(TimeInterval(minutes: Double(duration)))
+                let newDoseEntry = DoseEntry(
+                    type: .tempBasal,
+                    startDate: event.timestamp,
+                    endDate: currentEndDate,
+                    value: Double(value),
+                    unit: .units,
+                    deliveredUnits: Double(value),
+                    syncIdentifier: event.id,
+                    scheduledBasalRate: HKQuantity(
+                        unit: .internationalUnitsPerHour,
+                        doubleValue: Double(currentBasalRate.rate)
+                    ),
+                    insulinType: self.apsManager.pumpManager?.status.insulinType ?? nil,
+                    automatic: true,
+                    manuallyEntered: false,
+                    isMutable: false
+                )
+
+                // Add the new event entry to the result
+                insulinDoseEvents.append(newDoseEntry)
+            }
         }
         }
 
 
         return insulinDoseEvents
         return insulinDoseEvents