Skip to content

Commit 900125c

Browse files
authored
[LOOP-4470] refactoring bolus handling in bolus entry view (#548)
1 parent 693e7e8 commit 900125c

File tree

2 files changed

+60
-47
lines changed

2 files changed

+60
-47
lines changed

Loop/View Models/BolusEntryViewModel.swift

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,13 @@ final class BolusEntryViewModel: ObservableObject {
103103
private var manualGlucoseSample: NewGlucoseSample? // derived from `enteredManualGlucose`, but stored to ensure timestamp consistency
104104

105105
@Published var recommendedBolus: HKQuantity?
106+
var recommendedBolusAmount: Double? {
107+
recommendedBolus?.doubleValue(for: .internationalUnit())
108+
}
106109
@Published var enteredBolus = HKQuantity(unit: .internationalUnit(), doubleValue: 0)
110+
var enteredBolusAmount: Double {
111+
enteredBolus.doubleValue(for: .internationalUnit())
112+
}
107113
private var userChangedBolusAmount = false
108114
@Published var isInitiatingSaveOrBolus = false
109115

@@ -307,11 +313,11 @@ final class BolusEntryViewModel: ObservableObject {
307313
// MARK: - View API
308314

309315
var isBolusRecommended: Bool {
310-
guard let recommendedBolus = recommendedBolus else {
316+
guard let recommendedBolusAmount = recommendedBolusAmount else {
311317
return false
312318
}
313319

314-
return recommendedBolus.doubleValue(for: .internationalUnit()) > 0
320+
return recommendedBolusAmount > 0
315321
}
316322

317323
func saveAndDeliver(onSuccess completion: @escaping () -> Void) {
@@ -325,15 +331,13 @@ final class BolusEntryViewModel: ObservableObject {
325331
return
326332
}
327333

328-
let amountEntered = enteredBolus.doubleValue(for: .internationalUnit())
329-
330-
let amountToDeliver = delegate.roundBolusVolume(units: amountEntered)
331-
guard amountEntered == 0 || amountToDeliver > 0 else {
334+
let amountToDeliver = delegate.roundBolusVolume(units: enteredBolusAmount)
335+
guard enteredBolusAmount == 0 || amountToDeliver > 0 else {
332336
presentAlert(.bolusTooSmall)
333337
return
334338
}
335339

336-
let amountToDeliverString = bolusVolumeFormatter.numberFormatter.string(from: amountToDeliver) ?? String(amountToDeliver)
340+
let amountToDeliverString = formatBolusAmount(amountToDeliver)
337341

338342
// Capture state as is during initial action, to prevent race conditions
339343
// caused by later updates to member variables while async operations are
@@ -403,7 +407,7 @@ final class BolusEntryViewModel: ObservableObject {
403407
}
404408

405409
private func saveCarbsAndDeliverBolus(amountToDeliver: Double, potentialCarbEntry: NewCarbEntry?, onSuccess completion: @escaping () -> Void) {
406-
let activationType = BolusActivationType.activationTypeFor(recommendedAmount: recommendedBolus?.doubleValue(for: .internationalUnit()), bolusAmount: amountToDeliver)
410+
let activationType = BolusActivationType.activationTypeFor(recommendedAmount: recommendedBolusAmount, bolusAmount: amountToDeliver)
407411

408412
guard let carbEntry = potentialCarbEntry else {
409413
dosingDecision.carbEntry = nil
@@ -465,7 +469,12 @@ final class BolusEntryViewModel: ObservableObject {
465469
activeAlert = alert
466470
}
467471

468-
private lazy var bolusVolumeFormatter = QuantityFormatter(for: .internationalUnit())
472+
private lazy var bolusAmountFormatter: NumberFormatter = {
473+
let formatter = QuantityFormatter()
474+
formatter.setPreferredNumberFormatter(for: .internationalUnit())
475+
formatter.numberFormatter.roundingMode = .down
476+
return formatter.numberFormatter
477+
}()
469478

470479
private lazy var absorptionTimeFormatter: DateComponentsFormatter = {
471480
let formatter = DateComponentsFormatter()
@@ -477,15 +486,15 @@ final class BolusEntryViewModel: ObservableObject {
477486
}()
478487

479488
var enteredBolusAmountString: String {
480-
let bolusVolume = enteredBolus.doubleValue(for: .internationalUnit())
481-
return bolusVolumeFormatter.numberFormatter.string(from: bolusVolume) ?? String(bolusVolume)
489+
let bolusAmount = enteredBolusAmount
490+
return formatBolusAmount(bolusAmount)
482491
}
483492

484493
var maximumBolusAmountString: String? {
485-
guard let maxBolusVolume = maximumBolus?.doubleValue(for: .internationalUnit()) else {
494+
guard let maxBolusAmount = maximumBolus?.doubleValue(for: .internationalUnit()) else {
486495
return nil
487496
}
488-
return bolusVolumeFormatter.numberFormatter.string(from: maxBolusVolume) ?? String(maxBolusVolume)
497+
return formatBolusAmount(maxBolusAmount)
489498
}
490499

491500
var carbEntryAmountAndEmojiString: String? {
@@ -698,9 +707,11 @@ final class BolusEntryViewModel: ObservableObject {
698707
let notice: Notice?
699708
do {
700709
recommendation = try computeBolusRecommendation(from: state)
710+
701711
if let recommendation = recommendation {
702712
recommendedBolus = HKQuantity(unit: .internationalUnit(), doubleValue: delegate.roundBolusVolume(units: recommendation.amount))
703-
713+
//recommendedBolus = HKQuantity(unit: .internationalUnit(), doubleValue: recommendation.amount)
714+
704715
switch recommendation.notice {
705716
case .glucoseBelowSuspendThreshold:
706717
if let suspendThreshold = delegate.settings.suspendThreshold {
@@ -822,6 +833,25 @@ final class BolusEntryViewModel: ObservableObject {
822833

823834
chartDateInterval = DateInterval(start: chartStartDate, duration: .hours(totalHours))
824835
}
836+
837+
func formatBolusAmount(_ bolusAmount: Double) -> String {
838+
bolusAmountFormatter.string(from: bolusAmount) ?? String(bolusAmount)
839+
}
840+
841+
var recommendedBolusString: String {
842+
guard let amount = recommendedBolusAmount else {
843+
return ""
844+
}
845+
return formatBolusAmount(amount)
846+
}
847+
848+
func updateEnteredBolus(_ enteredBolusString: String) {
849+
updateEnteredBolus(bolusAmountFormatter.number(from: enteredBolusString)?.doubleValue)
850+
}
851+
852+
func updateEnteredBolus(_ enteredBolusAmount: Double?) {
853+
enteredBolus = HKQuantity(unit: .internationalUnit(), doubleValue: enteredBolusAmount ?? 0)
854+
}
825855
}
826856

827857
extension BolusEntryViewModel.Alert: Identifiable {

Loop/Views/BolusEntryView.swift

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@ struct BolusEntryView: View {
6363
.supportedInterfaceOrientations(.portrait)
6464
.alert(item: self.$viewModel.activeAlert, content: self.alert(for:))
6565
.onReceive(self.viewModel.$recommendedBolus) { recommendation in
66-
// If the recommendation changes, and the user has not edited the bolus amount, update the bolus amount
67-
let amount = recommendation?.doubleValue(for: .internationalUnit()) ?? 0
68-
if !editedBolusAmount {
69-
var newEnteredBolusString: String
70-
if amount == 0 {
71-
newEnteredBolusString = ""
72-
} else {
73-
newEnteredBolusString = Self.doseAmountFormatter.string(from: amount) ?? String(amount)
74-
}
75-
enteredBolusStringBinding.wrappedValue = newEnteredBolusString
66+
// If the user has not edited the bolus amount and there are recommendation changes, then update the bolus amount
67+
guard !editedBolusAmount else { return }
68+
69+
guard let amount = recommendation?.doubleValue(for: .internationalUnit()),
70+
amount != 0
71+
else {
72+
enteredBolusStringBinding.wrappedValue = ""
73+
return
7674
}
75+
76+
enteredBolusStringBinding.wrappedValue = viewModel.formatBolusAmount(amount)
7777
}
7878
.onReceive(self.viewModel.$isManualGlucoseEntryEnabled) { isManualGlucoseEntryEnabled in
7979
// The view model can disable manual glucose entry if CGM data returns.
@@ -270,19 +270,12 @@ struct BolusEntryView: View {
270270
}
271271
}
272272

273-
private static let doseAmountFormatter: NumberFormatter = {
274-
let quantityFormatter = QuantityFormatter()
275-
quantityFormatter.setPreferredNumberFormatter(for: .internationalUnit())
276-
quantityFormatter.numberFormatter.roundingMode = .down
277-
return quantityFormatter.numberFormatter
278-
}()
279-
280273
private var recommendedBolusRow: some View {
281274
HStack {
282275
Text("Recommended Bolus", comment: "Label for recommended bolus row on bolus screen")
283276
Spacer()
284277
HStack(alignment: .firstTextBaseline) {
285-
Text(recommendedBolusString)
278+
Text(viewModel.recommendedBolusString)
286279
.font(.title)
287280
.foregroundColor(Color(.label))
288281
bolusUnitsLabel
@@ -291,19 +284,9 @@ struct BolusEntryView: View {
291284
.accessibilityElement(children: .combine)
292285
}
293286

294-
private var recommendedBolusString: String {
295-
guard let amount = viewModel.recommendedBolus?.doubleValue(for: .internationalUnit()) else {
296-
return ""
297-
}
298-
return Self.doseAmountFormatter.string(from: amount) ?? String(amount)
299-
}
300-
301287
private func didBeginEditing() {
302288
if !editedBolusAmount {
303-
enteredBolusString = ""
304-
DispatchQueue.main.async {
305-
self.viewModel.enteredBolus = HKQuantity(unit: .internationalUnit(), doubleValue: 0)
306-
}
289+
enteredBolusStringBinding.wrappedValue = ""
307290
editedBolusAmount = true
308291
}
309292
}
@@ -315,7 +298,7 @@ struct BolusEntryView: View {
315298
HStack(alignment: .firstTextBaseline) {
316299
DismissibleKeyboardTextField(
317300
text: enteredBolusStringBinding,
318-
placeholder: Self.doseAmountFormatter.string(from: 0.0)!,
301+
placeholder: viewModel.formatBolusAmount(0.0),
319302
font: .preferredFont(forTextStyle: .title1),
320303
textColor: .loopAccent,
321304
textAlignment: .right,
@@ -338,10 +321,10 @@ struct BolusEntryView: View {
338321

339322
private var enteredBolusStringBinding: Binding<String> {
340323
Binding(
341-
get: { self.enteredBolusString },
324+
get: { enteredBolusString },
342325
set: { newValue in
343-
self.viewModel.enteredBolus = HKQuantity(unit: .internationalUnit(), doubleValue: Self.doseAmountFormatter.number(from: newValue)?.doubleValue ?? 0)
344-
self.enteredBolusString = newValue
326+
viewModel.updateEnteredBolus(newValue)
327+
enteredBolusString = newValue
345328
}
346329
)
347330
}

0 commit comments

Comments
 (0)