import Charts import CoreData import SwiftDate import SwiftUI import Swinject extension Stat { struct RootView: BaseView { let resolver: Resolver @StateObject var state = StateModel() @FetchRequest( entity: Readings.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate( format: "date >= %@", Calendar.current.startOfDay(for: Date()) as NSDate ) ) var fetchedGlucoseDay: FetchedResults @FetchRequest( entity: Readings.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-24.hours.timeInterval) as NSDate) ) var fetchedGlucoseTwentyFourHours: FetchedResults @FetchRequest( entity: Readings.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-7.days.timeInterval) as NSDate) ) var fetchedGlucoseWeek: FetchedResults @FetchRequest( entity: Readings.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate( format: "date > %@", Date().addingTimeInterval(-30.days.timeInterval) as NSDate ) ) var fetchedGlucoseMonth: FetchedResults @FetchRequest( entity: Readings.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate( format: "date > %@", Date().addingTimeInterval(-90.days.timeInterval) as NSDate ) ) var fetchedGlucose: FetchedResults @FetchRequest( entity: TDD.entity(), sortDescriptors: [NSSortDescriptor(key: "timestamp", ascending: false)] ) var fetchedTDD: FetchedResults @FetchRequest( entity: LoopStatRecord.entity(), sortDescriptors: [NSSortDescriptor(key: "start", ascending: false)], predicate: NSPredicate( format: "start > %@", Date().addingTimeInterval(-24.hours.timeInterval) as NSDate ) ) var fetchedLoopStats: FetchedResults @FetchRequest( entity: InsulinDistribution.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)] ) var fetchedInsulin: FetchedResults enum Duration: String, CaseIterable, Identifiable { case Today case Day case Week case Month case Total var id: Self { self } } @State private var selectedDuration: Duration = .Today @State var paddingAmount: CGFloat? = 10 @State var headline: Color = .secondary @State var days: Double = 0 @State var pointSize: CGFloat = 3 @State var conversionFactor = 0.0555 @ViewBuilder func stats() -> some View { if state.layingChart ?? true { bloodGlucose Divider() } else { bloodGlucose standingTIRchart Divider() } loops Divider() hba1c } @ViewBuilder func chart() -> some View { switch selectedDuration { case .Today: glucoseChart case .Day: glucoseChartTwentyFourHours case .Week: glucoseChartWeek case .Month: glucoseChartMonth case .Total: glucoseChart90 } if state.layingChart ?? true { tirChart } } var body: some View { ZStack { VStack(alignment: .center) { chart().padding(.top, 20) Divider() stats() Divider() Picker("Duration", selection: $selectedDuration) { ForEach(Duration.allCases) { duration in Text(duration.rawValue).tag(Optional(duration)) } } .pickerStyle(.segmented) } } .onAppear(perform: configureView) .navigationBarTitle("Statistics") .navigationBarTitleDisplayMode(.automatic) .navigationBarItems(leading: Button("Close", action: state.hideModal)) } var loops: some View { VStack { let loops_ = loopStats(fetchedLoopStats) HStack { ForEach(0 ..< loops_.count, id: \.self) { index in VStack { Text(loops_[index].string).font(.subheadline).foregroundColor(.secondary) Text( index == 0 ? loops_[index].double.formatted() : ( index == 2 ? loops_[index].double .formatted(.number.grouping(.never).rounded().precision(.fractionLength(2))) : loops_[index] .double .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) ) ) }.padding(.horizontal, 6) } } } } var hba1c: some View { let useUnit: GlucoseUnits = (state.units == .mmolL && (state.overrideUnit ?? false)) ? .mgdL : (state.units == .mgdL && (state.overrideUnit ?? false) || state.units == .mmolL) ? .mmolL : .mgdL return HStack { let hba1cs = glucoseStats(fetchedGlucose) let hba1cString = ( useUnit == .mmolL ? hba1cs.ifcc .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) : hba1cs.ngsp .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) + " %" ) VStack { Text("HbA1C").font(.subheadline).foregroundColor(headline) HStack { VStack { Text(hba1cString) } } }.padding([.horizontal], 15) VStack { Text("SD").font(.subheadline).foregroundColor(.secondary) HStack { VStack { Text( hba1cs.sd .formatted( .number.grouping(.never).rounded() .precision(.fractionLength(state.units == .mmolL ? 1 : 0)) ) ) } } }.padding([.horizontal], 15) VStack { Text("CV").font(.subheadline).foregroundColor(.secondary) HStack { VStack { Text( hba1cs.cv.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) ) } } }.padding([.horizontal], 15) // if selectedDuration == .Total || selectedDuration == .Today { VStack { Text("Days").font(.subheadline).foregroundColor(.secondary) HStack { VStack { Text(numberOfDays.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))) } } }.padding([.horizontal], 15) // } } } var bloodGlucose: some View { VStack { HStack { let bgs = glucoseStats(fetchedGlucose) VStack { HStack { Text(selectedDuration == .Today ? "Readings today" : "Readings / 24h").font(.subheadline) .foregroundColor(.secondary) } HStack { VStack { Text( bgs.readings.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) ) } } } VStack { HStack { Text("Average").font(.subheadline).foregroundColor(headline) } HStack { VStack { Text( bgs.average .formatted( .number.grouping(.never).rounded() .precision(.fractionLength(state.units == .mmolL ? 1 : 0)) ) ) } } } VStack { HStack { Text("Median").font(.subheadline).foregroundColor(.secondary) } HStack { VStack { Text( bgs.median .formatted( .number.grouping(.never).rounded() .precision(.fractionLength(state.units == .mmolL ? 1 : 0)) ) ) } } } } } } var numberOfDays: Double { let array = selectedDuration == .Today ? fetchedGlucoseDay : selectedDuration == .Day ? fetchedGlucoseTwentyFourHours : selectedDuration == .Week ? fetchedGlucoseWeek : selectedDuration == .Month ? fetchedGlucoseMonth : selectedDuration == .Total ? fetchedGlucose : fetchedGlucoseDay let endIndex = array.count - 1 var days = 0.0 if endIndex > 0 { let firstElementTime = fetchedGlucose.first?.date ?? Date() let lastElementTime = fetchedGlucose[endIndex].date ?? Date() days = (firstElementTime - lastElementTime).timeInterval / 8.64E4 } return days } var tirChart: some View { let array = selectedDuration == .Today ? fetchedGlucoseDay : selectedDuration == .Day ? fetchedGlucoseTwentyFourHours : selectedDuration == .Week ? fetchedGlucoseWeek : selectedDuration == .Month ? fetchedGlucoseMonth : selectedDuration == .Total ? fetchedGlucose : fetchedGlucoseDay let fetched = tir(array) let data: [ShapeModel] = [ .init(type: "Low", percent: fetched[0].decimal), .init(type: "In Range", percent: fetched[1].decimal), .init(type: "High", percent: fetched[2].decimal) ] return Chart(data) { shape in BarMark( x: .value("TIR", shape.percent) ) .foregroundStyle(by: .value("Group", shape.type)) .annotation(position: .overlay, alignment: .center) { Text( shape.percent == 0 ? "" : shape .percent < 12 ? "\(shape.percent, format: .number.precision(.fractionLength(0)))" : "\(shape.percent, format: .number.precision(.fractionLength(0))) %" ) } } .chartXAxis(.hidden) .chartForegroundStyleScale(["Low": .red, "In Range": .green, "High": .orange]).frame(maxHeight: 55) } var standingTIRchart: some View { let array = selectedDuration == .Today ? fetchedGlucoseDay : selectedDuration == .Day ? fetchedGlucoseTwentyFourHours : selectedDuration == .Week ? fetchedGlucoseWeek : selectedDuration == .Month ? fetchedGlucoseMonth : selectedDuration == .Total ? fetchedGlucose : fetchedGlucoseDay let fetched = tir(array) let data: [ShapeModel] = [ .init(type: "Low", percent: fetched[0].decimal), .init(type: "In Range", percent: fetched[1].decimal), .init(type: "High", percent: fetched[2].decimal) ] return VStack(alignment: .center) { Chart(data) { shape in BarMark( x: .value("Shape", shape.type), y: .value("Percentage", shape.percent) ) .foregroundStyle(by: .value("Group", shape.type)) .annotation(position: shape.percent < 5 ? .top : .overlay, alignment: .center) { Text(shape.percent == 0 ? "" : "\(shape.percent, format: .number.precision(.fractionLength(0))) %") } } .chartYAxis(.hidden) .chartLegend(.hidden) .chartForegroundStyleScale(["Low": .red, "In Range": .green, "High": .orange]) } } var glucoseChart: some View { let count = fetchedGlucoseDay.count let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) return Chart { ForEach(fetchedGlucoseDay.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.orange) .symbolSize(count < 20 ? 30 : 12) } ForEach( fetchedGlucoseDay .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }), id: \.date ) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.green) .symbolSize(count < 20 ? 30 : 12) } ForEach(fetchedGlucoseDay.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.red) .symbolSize(count < 20 ? 30 : 12) } } .chartYAxis { AxisMarks( values: [ 0, lowLimit, highLimit, state.units == .mmolL ? 15 : 270 ] ) } } var glucoseChartTwentyFourHours: some View { let count = fetchedGlucoseTwentyFourHours.count let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) return Chart { ForEach(fetchedGlucoseTwentyFourHours.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.orange) .symbolSize(count < 20 ? 20 : 10) } ForEach( fetchedGlucoseTwentyFourHours .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }), id: \.date ) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.green) .symbolSize(count < 20 ? 20 : 10) } ForEach(fetchedGlucoseTwentyFourHours.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.red) .symbolSize(count < 20 ? 20 : 10) } } .chartYAxis { AxisMarks( values: [ 0, lowLimit, highLimit, state.units == .mmolL ? 15 : 270 ] ) } } var glucoseChartWeek: some View { let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) return Chart { ForEach(fetchedGlucoseWeek.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.orange) .symbolSize(5) } ForEach( fetchedGlucoseWeek .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }), id: \.date ) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.green) .symbolSize(5) } ForEach(fetchedGlucoseWeek.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.red) .symbolSize(5) } } .chartYAxis { AxisMarks( values: [ 0, lowLimit, highLimit, state.units == .mmolL ? 15 : 270 ] ) } } var glucoseChartMonth: some View { let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) return Chart { ForEach(fetchedGlucoseMonth.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.orange) .symbolSize(2) } ForEach( fetchedGlucoseMonth .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }), id: \.date ) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.green) .symbolSize(2) } ForEach(fetchedGlucoseMonth.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.red) .symbolSize(2) } } .chartYAxis { AxisMarks( values: [ 0, lowLimit, highLimit, state.units == .mmolL ? 15 : 270 ] ) } } var glucoseChart90: some View { let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1) return Chart { ForEach(fetchedGlucose.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.orange) .symbolSize(2) } ForEach( fetchedGlucose .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }), id: \.date ) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.green) .symbolSize(2) } ForEach(fetchedGlucose.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in PointMark( x: .value("Date", item.date ?? Date()), y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1)) ) .foregroundStyle(.red) .symbolSize(2) } } .chartYAxis { AxisMarks( values: [ 0, lowLimit, highLimit, state.units == .mmolL ? 15 : 270 ] ) } } private func loopStats(_ loops: FetchedResults) -> [(double: Double, string: String)] { guard (loops.first?.start) != nil else { return [] } var i = 0.0 var minimumInt = 999.0 var maximumInt = 0.0 var timeIntervalLoops = 0.0 var previousTimeLoop = loops.first?.end ?? Date() var timeIntervalLoopArray: [Double] = [] let durationArray = loops.compactMap({ each in each.duration }) let durationArrayCount = durationArray.count // var durationAverage = durationArray.reduce(0, +) / Double(durationArrayCount) let medianDuration = medianCalculationDouble(array: durationArray) let successsNR = loops.compactMap({ each in each.loopStatus }).filter({ each in each!.contains("Success") }).count let errorNR = durationArrayCount - successsNR let successRate: Double? = (Double(successsNR) / Double(successsNR + errorNR)) * 100 for each in loops { if let loopEnd = each.end { i += 1 timeIntervalLoops = (previousTimeLoop - (each.start ?? previousTimeLoop)).timeInterval / 60 if timeIntervalLoops > 0.0, i != 1 { timeIntervalLoopArray.append(timeIntervalLoops) } if timeIntervalLoops > maximumInt { maximumInt = timeIntervalLoops } if timeIntervalLoops < minimumInt, i != 1 { minimumInt = timeIntervalLoops } previousTimeLoop = loopEnd } } // Average Loop Interval in minutes let timeOfFirstIndex = loops.first?.start ?? Date() let lastIndexWithTimestamp = loops.count - 1 let timeOfLastIndex = loops[lastIndexWithTimestamp].end ?? Date() let averageInterval = (timeOfFirstIndex - timeOfLastIndex).timeInterval / 60 / Double(errorNR + successsNR) if minimumInt == 999.0 { minimumInt = 0.0 } var array: [(double: Double, string: String)] = [] array.append((double: Double(successsNR + errorNR), string: "Loops")) array.append((double: averageInterval, string: "Interval")) array.append((double: medianDuration, string: "Duration")) array.append((double: successRate ?? 100, string: "%")) return array } private func medianCalculation(array: [Int]) -> Double { guard !array.isEmpty else { return 0 } let sorted = array.sorted() let length = array.count if length % 2 == 0 { return Double((sorted[length / 2 - 1] + sorted[length / 2]) / 2) } return Double(sorted[length / 2]) } private func medianCalculationDouble(array: [Double]) -> Double { guard !array.isEmpty else { return 0 } let sorted = array.sorted() let length = array.count if length % 2 == 0 { return (sorted[length / 2 - 1] + sorted[length / 2]) / 2 } return sorted[length / 2] } private func glucoseStats(_ glucose_90: FetchedResults) -> (ifcc: Double, ngsp: Double, average: Double, median: Double, sd: Double, cv: Double, readings: Double) { var numberOfDays: Double = 0 let endIndex = glucose_90.count - 1 if endIndex > 0 { let firstElementTime = glucose_90[0].date ?? Date() let lastElementTime = glucose_90[endIndex].date ?? Date() numberOfDays = (firstElementTime - lastElementTime).timeInterval / 8.64E4 } var duration = 1 var denominator: Double = 1 switch selectedDuration { case .Today: let minutesSinceMidnight = Calendar.current.component(.hour, from: Date()) * 60 + Calendar.current .component(.minute, from: Date()) duration = minutesSinceMidnight denominator = 1 case .Day: duration = 1 * 1440 denominator = 1 case .Week: duration = 7 * 1440 if numberOfDays > 7 { denominator = 7 } else { denominator = numberOfDays } case .Month: duration = 30 * 1440 if numberOfDays > 30 { denominator = 30 } else { denominator = numberOfDays } case .Total: duration = 90 * 1440 if numberOfDays >= 90 { denominator = 90 } else { denominator = numberOfDays } } let timeAgo = Date().addingTimeInterval(-duration.minutes.timeInterval) let glucose = glucose_90.filter({ ($0.date ?? Date()) >= timeAgo }) let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) }) let sumReadings = justGlucoseArray.reduce(0, +) let countReadings = justGlucoseArray.count let glucoseAverage = Double(sumReadings) / Double(countReadings) let medianGlucose = medianCalculation(array: justGlucoseArray) var NGSPa1CStatisticValue = 0.0 var IFCCa1CStatisticValue = 0.0 if numberOfDays > 0 { NGSPa1CStatisticValue = (glucoseAverage + 46.7) / 28.7 // NGSP (%) IFCCa1CStatisticValue = 10.929 * (NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol) A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15) } var sumOfSquares = 0.0 for array in justGlucoseArray { sumOfSquares += pow(Double(array) - Double(glucoseAverage), 2) } var sd = 0.0 var cv = 0.0 // Avoid division by zero if glucoseAverage > 0 { sd = sqrt(sumOfSquares / Double(countReadings)) cv = sd / Double(glucoseAverage) * 100 } var output: (ifcc: Double, ngsp: Double, average: Double, median: Double, sd: Double, cv: Double, readings: Double) output = ( ifcc: IFCCa1CStatisticValue, ngsp: NGSPa1CStatisticValue, average: glucoseAverage * (state.units == .mmolL ? conversionFactor : 1), median: medianGlucose * (state.units == .mmolL ? conversionFactor : 1), sd: sd * (state.units == .mmolL ? conversionFactor : 1), cv: cv, readings: Double(countReadings) / denominator ) return output } private func tir(_ glucose_90: FetchedResults) -> [(decimal: Decimal, string: String)] { var duration = 1 switch selectedDuration { case .Today: let minutesSinceMidnight = Calendar.current.component(.hour, from: Date()) * 60 + Calendar.current .component(.minute, from: Date()) duration = minutesSinceMidnight case .Day: duration = 1 * 1440 case .Week: duration = 7 * 1440 case .Month: duration = 30 * 1440 case .Total: duration = 90 * 1440 } let hypoLimit = Int(state.lowLimit ?? 70) let hyperLimit = Int(state.highLimit ?? 145) let timeAgo = Date().addingTimeInterval(-duration.minutes.timeInterval) let glucose = glucose_90.filter({ ($0.date ?? Date()) >= timeAgo }) let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) }) let totalReadings = justGlucoseArray.count let hyperArray = glucose.filter({ $0.glucose >= hyperLimit }) let hyperReadings = hyperArray.compactMap({ each in each.glucose as Int16 }).count let hyperPercentage = Double(hyperReadings) / Double(totalReadings) * 100 let hypoArray = glucose.filter({ $0.glucose <= hypoLimit }) let hypoReadings = hypoArray.compactMap({ each in each.glucose as Int16 }).count let hypoPercentage = Double(hypoReadings) / Double(totalReadings) * 100 let tir = 100 - (hypoPercentage + hyperPercentage) var array: [(decimal: Decimal, string: String)] = [] array.append((decimal: Decimal(hypoPercentage), string: "Low")) array.append((decimal: Decimal(tir), string: "NormaL")) array.append((decimal: Decimal(hyperPercentage), string: "High")) return array } private func colorOfGlucose(_ index: Int) -> Color { let whichIndex = index switch whichIndex { case 0: return .red case 1: return .green case 2: return .orange default: return .primary } } } }