diff --git a/Loop Complications/ComplicationSnapshot.swift b/Loop Complications/ComplicationSnapshot.swift new file mode 100644 index 0000000000..5fc0e1acc8 --- /dev/null +++ b/Loop Complications/ComplicationSnapshot.swift @@ -0,0 +1,168 @@ +// +// ComplicationSnapshot.swift +// Loop Complications +// +// What the watch's complications show, published by the watch app into the shared app group and +// read by the Loop Complications widget extension. Compiled into both (and into LoopTests). Strings +// arrive already formatted and localized by the app — the extension links nothing of Loop's — so +// the widget only decides, per moment, which of them to show. +// + +import Foundation + +struct ComplicationSnapshot: Codable, Equatable { + /// The reading, formatted for the display unit, or the sensor's condition ("LOW", "HIGH"). + var glucoseText: String? + var trendSymbol: String? + /// Shown in place of the reading once it is stale (stock's localized "---"). + var staleGlucoseText: String = "---" + /// After this, the reading is stale (stock: reading date + LoopAlgorithm.inputDataRecencyInterval). + var glucoseStaleAt: Date? + var eventualText: String? + /// The wide inline line in stock's localized "UtilitarianLargeFlat" format + /// ("106↗ 108 8:47 PM"), and the same line with the reading stale. + var inlineText: String? + var inlineStaleText: String? + /// When the loop last completed, and where its colour changes (LoopCompletionFreshness: fresh ≤ 6 min, + /// aging ≤ 16 min, then stale) — computed by the app so the constants live in one place. + var loopDate: Date? + var freshUntil: Date? + var agingUntil: Date? + /// What the glucose graph draws; the widget renders it at its slot's size (nil: no graph). + var graph: ComplicationGraph? + + enum Freshness { case fresh, aging, stale } + + func freshness(at date: Date) -> Freshness { + guard loopDate != nil else { return .stale } + if let fresh = freshUntil, date <= fresh { return .fresh } + if let aging = agingUntil, date <= aging { return .aging } + return .stale + } + + func glucoseIsStale(at date: Date) -> Bool { + glucoseStaleAt.map { date > $0 } ?? true + } + + /// The reading as shown at `date`: the value, or the stale marker. + func glucose(at date: Date) -> String { + glucoseIsStale(at: date) ? staleGlucoseText : (glucoseText ?? staleGlucoseText) + } + + /// The trend symbol, blank once the reading is stale (as stock). + func trend(at date: Date) -> String { + glucoseIsStale(at: date) ? "" : (trendSymbol ?? "") + } + + func inline(at date: Date) -> String { + (glucoseIsStale(at: date) ? inlineStaleText : inlineText) ?? glucose(at: date) + } + + /// The moments after `date` at which something visible changes: the reading goes stale, the loop + /// colour turns. The widget's timeline carries one entry for each, so no reload is needed for them. + func changeMoments(after date: Date) -> [Date] { + [glucoseStaleAt.map { $0.addingTimeInterval(1) }, freshUntil.map { $0.addingTimeInterval(1) }, + agingUntil.map { $0.addingTimeInterval(1) }] + .compactMap { $0 }.filter { $0 > date }.sorted() + } + + // MARK: - Shared storage + + static let defaultsKey = "ComplicationSnapshot" + + /// The app group both targets share, from each one's own Info.plist. + static var appGroupIdentifier: String? { + Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String + } + + static var sharedDefaults: UserDefaults? { appGroupIdentifier.flatMap { UserDefaults(suiteName: $0) } } + + static func load(from defaults: UserDefaults? = sharedDefaults) -> ComplicationSnapshot? { + defaults?.data(forKey: defaultsKey).flatMap { try? JSONDecoder().decode(Self.self, from: $0) } + } + + func save(to defaults: UserDefaults? = Self.sharedDefaults) { + if let data = try? JSONEncoder().encode(self) { defaults?.set(data, forKey: Self.defaultsKey) } + } + + static var sample: ComplicationSnapshot { + let now = Date() + return ComplicationSnapshot(glucoseText: "106", trendSymbol: "↗", glucoseStaleAt: now.addingTimeInterval(15 * 60), + eventualText: "108", inlineText: "106↗ 108", inlineStaleText: "--- 108", + loopDate: now, freshUntil: now.addingTimeInterval(6 * 60), + agingUntil: now.addingTimeInterval(16 * 60), graph: .sample(now: now)) + } +} + +/// The glucose graph in the display unit, as plain numbers, so the widget can draw it at its own size. +struct ComplicationGraph: Codable, Equatable { + struct Point: Codable, Equatable { + var date: Date + var value: Double + var isManual = false + } + + struct Band: Codable, Equatable { + var start: Date + var end: Date + var min: Double + var max: Double + } + + /// The span shown on each side of the moment drawn, as stock's ClockKit graph (4 h centered on now). + static let halfSpan: TimeInterval = 2 * 60 * 60 + + var glucoseMin: Double + var glucoseMax: Double + var minLabel: String + var maxLabel: String + var targetRanges: [Band] + /// The active override's target, over its active interval. + var override: Band? + var historical: [Point] + var predicted: [Point] + + static func sample(now: Date) -> ComplicationGraph { + let historical = (0..<24).map { i in + Point(date: now.addingTimeInterval(TimeInterval(i - 24) * 300), value: 110 + 25 * sin(Double(i) / 4)) + } + let predicted = (0..<24).map { i in + Point(date: now.addingTimeInterval(TimeInterval(i) * 300), value: 120 - Double(i)) + } + return ComplicationGraph(glucoseMin: 75, glucoseMax: 150, minLabel: "75", maxLabel: "150", + targetRanges: [Band(start: now.addingTimeInterval(-halfSpan), end: now.addingTimeInterval(halfSpan), + min: 100, max: 110)], + override: nil, historical: historical, predicted: predicted) + } +} + +/// The widget kinds, shared with the watch app (its ClockKit migrator maps old complications to them). +enum LoopComplicationKind { + static let loopStatus = "LoopStatus" + static let glucoseGraph = "GlucoseGraph" + + static let all = [loopStatus, glucoseGraph] +} + +/// When the watch app should ask WidgetKit for a reload. Reloads are budgeted by the system, so one is +/// requested only when the snapshot changed and at most every `minimumInterval`; a change inside the +/// window is not lost — the owed reload is paid on the next offer once the window has passed. +struct ComplicationReloadThrottle { + var minimumInterval: TimeInterval = 5 * 60 + private(set) var lastPublished: Snapshot? + private(set) var lastReloadAt = Date.distantPast + private(set) var reloadOwed = false + + /// `save`: the snapshot changed and should be written. `reload`: ask WidgetKit now. + mutating func offer(_ snapshot: Snapshot, now: Date) -> (save: Bool, reload: Bool) { + let changed = snapshot != lastPublished + if changed { + lastPublished = snapshot + reloadOwed = true + } + guard reloadOwed, now.timeIntervalSince(lastReloadAt) >= minimumInterval else { return (changed, false) } + reloadOwed = false + lastReloadAt = now + return (changed, true) + } +} diff --git a/Loop Complications/Info.plist b/Loop Complications/Info.plist new file mode 100644 index 0000000000..d812295954 --- /dev/null +++ b/Loop Complications/Info.plist @@ -0,0 +1,31 @@ + + + + + AppGroupIdentifier + $(APP_GROUP_IDENTIFIER) + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Loop + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + $(LOOP_MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/Loop Complications/LoopComplications.entitlements b/Loop Complications/LoopComplications.entitlements new file mode 100644 index 0000000000..d9849a816d --- /dev/null +++ b/Loop Complications/LoopComplications.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + $(APP_GROUP_IDENTIFIER) + + + diff --git a/Loop Complications/LoopComplications.swift b/Loop Complications/LoopComplications.swift new file mode 100644 index 0000000000..5d278b0c53 --- /dev/null +++ b/Loop Complications/LoopComplications.swift @@ -0,0 +1,214 @@ +// +// LoopComplications.swift +// Loop Complications (watchOS widget extension) +// +// Loop's watch complications on WidgetKit, replacing the ClockKit ones (ClockKit is deprecated and +// current watchOS no longer takes new ClockKit complications). The same two as before: +// "Loop Status" — the reading, its trend and the loop's freshness — and "Glucose Graph" — the +// reading over the chart of recent and predicted glucose. The watch app publishes a +// ComplicationSnapshot (with the graph's data) to the shared app group and asks WidgetKit to reload; +// this extension only reads it, lays it out and draws the graph at its slot's size. +// + +import SwiftUI +import WidgetKit + +struct ComplicationEntry: TimelineEntry { + let date: Date + let snapshot: ComplicationSnapshot? +} + +struct ComplicationProvider: TimelineProvider { + func placeholder(in context: Context) -> ComplicationEntry { + ComplicationEntry(date: Date(), snapshot: .sample) + } + + func getSnapshot(in context: Context, completion: @escaping (ComplicationEntry) -> Void) { + completion(ComplicationEntry(date: Date(), snapshot: context.isPreview ? .sample : ComplicationSnapshot.load())) + } + + /// Now, then each moment something visible changes on its own (the reading goes stale, the loop's + /// colour turns). New data arrives by the watch app asking for a reload. + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + let now = Date() + let snapshot = ComplicationSnapshot.load() + let moments = [now] + (snapshot?.changeMoments(after: now) ?? []) + completion(Timeline(entries: moments.map { ComplicationEntry(date: $0, snapshot: snapshot) }, policy: .never)) + } +} + +// MARK: - Shared look + +extension ComplicationSnapshot.Freshness { + /// The watch app's colours: its "tint" asset, the warning yellow, the HIG red. + var color: Color { + switch self { + case .fresh: return Color(red: 0.298, green: 0.851, blue: 0.392) + case .aging: return .yellow + case .stale: return Color(red: 1, green: 59 / 255, blue: 48 / 255) + } + } +} + +extension View { + /// watchOS 10+ draws a placeholder in place of any widget that does not declare a container + /// background — every family except inline. + func complicationBackground() -> some View { + containerBackground(for: .widget) { Color.clear } + } +} + +// MARK: - Loop Status + +struct LoopStatusView: View { + let entry: ComplicationEntry + @Environment(\.widgetFamily) private var family + + private var snapshot: ComplicationSnapshot { entry.snapshot ?? ComplicationSnapshot() } + private var glucose: String { snapshot.glucose(at: entry.date) } + private var trend: String { snapshot.trend(at: entry.date) } + private var freshness: ComplicationSnapshot.Freshness { snapshot.freshness(at: entry.date) } + + var body: some View { + content.complicationBackground() + } + + @ViewBuilder private var content: some View { + switch family { + case .accessoryInline: + Text(snapshot.inline(at: entry.date)) + case .accessoryCorner: + // The face curves the label along the bezel; the tip carries the loop's ring. + Circle() + .strokeBorder(freshness.color, lineWidth: 3) + .widgetAccentable() + .widgetLabel { Text(glucose + trend) } + default: + // Circular: stock's open gauge in the loop's colour, the reading inside, the trend below. + ZStack { + AccessoryWidgetBackground() + Circle() + .strokeBorder(freshness.color, lineWidth: 3) + .widgetAccentable() + VStack(spacing: -2) { + Text(glucose) + .font(.system(size: 18, weight: .semibold, design: .rounded)) + .minimumScaleFactor(0.5).lineLimit(1) + Text(trend).font(.system(size: 12, weight: .medium)) + } + .padding(4) + } + } + } +} + +struct LoopStatusWidget: Widget { + var body: some WidgetConfiguration { + StaticConfiguration(kind: LoopComplicationKind.loopStatus, provider: ComplicationProvider()) { entry in + LoopStatusView(entry: entry) + } + // Plain Strings: an interpolated literal is formatted text, which WidgetKit rejects here. + .configurationDisplayName(NSLocalizedString("Loop Status", comment: "Watch complication name: reading, trend and loop freshness")) + .description(NSLocalizedString("Your glucose, its trend and how recently Loop ran.", comment: "Watch complication description: Loop Status")) + .supportedFamilies([.accessoryInline, .accessoryCorner, .accessoryCircular]) + } +} + +// MARK: - Glucose Graph + +struct GlucoseGraphView: View { + let entry: ComplicationEntry + + private var snapshot: ComplicationSnapshot { entry.snapshot ?? ComplicationSnapshot() } + + var body: some View { + // Stock's layout: the reading and its trend, the chart under it. + VStack(alignment: .leading, spacing: 1) { + Text(snapshot.glucose(at: entry.date) + snapshot.trend(at: entry.date)) + .font(.headline) + .widgetAccentable() + if let graph = snapshot.graph { + GlucoseGraphCanvas(graph: graph, now: entry.date) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .complicationBackground() + } +} + +/// Stock's ClockKit chart (ComplicationChartManager), drawn at whatever size the slot leaves it. +struct GlucoseGraphCanvas: View { + let graph: ComplicationGraph + let now: Date + + /// The watch app's "glucose" asset. + private static let glucoseColor = Color(red: 0.310, green: 0.678, blue: 0.973) + + var body: some View { + Canvas { context, size in + let start = now.addingTimeInterval(-ComplicationGraph.halfSpan) + let end = now.addingTimeInterval(ComplicationGraph.halfSpan) + let valueSpan = max(graph.glucoseMax - graph.glucoseMin, 1) + + func x(_ date: Date) -> CGFloat { + CGFloat(date.timeIntervalSince(start) / end.timeIntervalSince(start)) * size.width + } + func y(_ value: Double) -> CGFloat { + CGFloat((graph.glucoseMax - value) / valueSpan) * size.height + } + func fill(_ band: ComplicationGraph.Band, until bandEnd: Date, opacity: Double) { + let left = x(max(start, band.start)), right = x(min(end, bandEnd)) + guard right > left else { return } + let top = y(band.max) + let rect = CGRect(x: left, y: top, width: right - left, height: max(y(band.min) - top, 2)) + context.fill(Path(rect), with: .color(Self.glucoseColor.opacity(opacity))) + } + + for band in graph.targetRanges { + fill(band, until: band.end, opacity: graph.override != nil ? 0.2 : 0.3) + } + if let override = graph.override { + fill(override, until: override.end, opacity: 0.4) + if end > override.end { + fill(override, until: end, opacity: 0.25) + } + } + + for point in graph.historical where point.date >= start && point.date <= end { + let side: CGFloat = point.isManual ? 3 : 2 + context.fill(Path(CGRect(x: x(point.date), y: y(point.value), width: side, height: side)), + with: .color(point.isManual ? .red : Self.glucoseColor)) + } + + if graph.predicted.count > 2 { + var path = Path() + path.addLines(graph.predicted.map { CGPoint(x: x($0.date), y: y($0.value)) }) + context.stroke(path, with: .color(.white), style: StrokeStyle(lineWidth: 1, dash: [5, 3], dashPhase: 11)) + } + + let label = { (text: String) in Text(text).font(.system(size: 10)).foregroundColor(.white) } + context.draw(label(graph.maxLabel), at: CGPoint(x: size.width - 2, y: 2), anchor: .topTrailing) + context.draw(label(graph.minLabel), at: CGPoint(x: size.width - 2, y: size.height - 2), anchor: .bottomTrailing) + } + } +} + +struct GlucoseGraphWidget: Widget { + var body: some WidgetConfiguration { + StaticConfiguration(kind: LoopComplicationKind.glucoseGraph, provider: ComplicationProvider()) { entry in + GlucoseGraphView(entry: entry) + } + .configurationDisplayName(NSLocalizedString("Glucose Graph", comment: "Watch complication name: glucose chart")) + .description(NSLocalizedString("Your glucose over the last two hours and Loop's prediction.", comment: "Watch complication description: Glucose Graph")) + .supportedFamilies([.accessoryRectangular]) + } +} + +@main +struct LoopComplicationsBundle: WidgetBundle { + var body: some Widget { + LoopStatusWidget() + GlucoseGraphWidget() + } +} diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index b147fc93e2..e081e11f2f 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -7,49 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - 01414522FC033F2B7A9B223A /* Loop/Resources/Sounds/crying.caf in Resources */ = {isa = PBXBuildFile; fileRef = 85203550670F2E9BE92F27D8 /* Loop/Resources/Sounds/crying.caf */; }; - 0A02E77AD7536C322933D49B /* Loop/Resources/Sounds/minimal.caf in Resources */ = {isa = PBXBuildFile; fileRef = 74145BE3C46270883D295A1F /* Loop/Resources/Sounds/minimal.caf */; }; - 124B300527D84BE2A1233A91 /* Loop/Resources/Sounds/urgent_low.caf in Resources */ = {isa = PBXBuildFile; fileRef = C9EA3F48338B49BD9C6BD0F9 /* Loop/Resources/Sounds/urgent_low.caf */; }; - 7877942ED674BA37847514AD /* Loop/Resources/Sounds/acoustic_hg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = A9B1F564432BCBF70C0C6CBB /* Loop/Resources/Sounds/acoustic_hg_harsh.caf */; }; - A82DB18B916DC17D2E180D59 /* Loop/Resources/Sounds/acoustic_hg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3481DFC7974E27ABE6AAC4C3 /* Loop/Resources/Sounds/acoustic_hg_medium.caf */; }; - 4A208427654F9A249EC2CFDB /* Loop/Resources/Sounds/acoustic_hg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 1DCC85EA42C8D22C1EC824D8 /* Loop/Resources/Sounds/acoustic_hg_mild.caf */; }; - F44BED105495ABA988BAB05F /* Loop/Resources/Sounds/acoustic_lg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = A5E01C8397D139F93B60CA0A /* Loop/Resources/Sounds/acoustic_lg_harsh.caf */; }; - B0007FE66FB0FE886FACD77D /* Loop/Resources/Sounds/acoustic_lg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = D64AC8FAA2340810ECB85AE9 /* Loop/Resources/Sounds/acoustic_lg_medium.caf */; }; - D0EBF80B1D85F96B156725AA /* Loop/Resources/Sounds/acoustic_lg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 65E7CB20BE6AB6A39AF072A2 /* Loop/Resources/Sounds/acoustic_lg_mild.caf */; }; - 3461DE9DBC0B97D5662244FB /* Loop/Resources/Sounds/acoustic_lgs_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = 688FE8312F66C4C1909FF93F /* Loop/Resources/Sounds/acoustic_lgs_harsh.caf */; }; - F98A16085E9F216CEDDB18DB /* Loop/Resources/Sounds/acoustic_lgs_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = E5D3EF6AEBD5FFAF2ABB187B /* Loop/Resources/Sounds/acoustic_lgs_medium.caf */; }; - 182579BE820905E2B9FCC1B4 /* Loop/Resources/Sounds/acoustic_lgs_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 16BE482D6AF35127A36FD9EB /* Loop/Resources/Sounds/acoustic_lgs_mild.caf */; }; - 6AB8817E7C134FE630492390 /* Loop/Resources/Sounds/acoustic_ulg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = A96BF0E0B93A88D6BCEC2338 /* Loop/Resources/Sounds/acoustic_ulg_harsh.caf */; }; - BAD8333EC083569038F10E88 /* Loop/Resources/Sounds/acoustic_ulg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = CC3E2E706E45342C823D641F /* Loop/Resources/Sounds/acoustic_ulg_medium.caf */; }; - 6E0CBD04FE69B38497EFC6F0 /* Loop/Resources/Sounds/acoustic_ulg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = E38835D52BBD5607D90B9615 /* Loop/Resources/Sounds/acoustic_ulg_mild.caf */; }; - DB1B4A63BB3DE5DF05D56791 /* Loop/Resources/Sounds/electronic_hg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = 2D388DFB60E5960C857807D0 /* Loop/Resources/Sounds/electronic_hg_harsh.caf */; }; - 0EA4FC2C9D24030B0D586F80 /* Loop/Resources/Sounds/electronic_hg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 987F0560C0D00A3F1AC4D006 /* Loop/Resources/Sounds/electronic_hg_medium.caf */; }; - 09789816D7760CF9BAA96F28 /* Loop/Resources/Sounds/electronic_hg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = C8BD9DF4B53E440810325EA6 /* Loop/Resources/Sounds/electronic_hg_mild.caf */; }; - D1530992424BA31F0A44EA27 /* Loop/Resources/Sounds/electronic_lg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = FE620B9DFF719ED07CEEA1B4 /* Loop/Resources/Sounds/electronic_lg_harsh.caf */; }; - 3E46B514B1D95CD39E91724B /* Loop/Resources/Sounds/electronic_lg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0F7A07E85B57B85C219B1244 /* Loop/Resources/Sounds/electronic_lg_medium.caf */; }; - C9489985ABD6FED5A2E5C857 /* Loop/Resources/Sounds/electronic_lg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = C6BD81B55371D7597A3D2BCC /* Loop/Resources/Sounds/electronic_lg_mild.caf */; }; - B63CAEC6D7662917D8EF89A7 /* Loop/Resources/Sounds/electronic_lgs_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = 177CE52DCE2BF26A5B8D4FCF /* Loop/Resources/Sounds/electronic_lgs_harsh.caf */; }; - 746A339E8C3350AB42CC5A16 /* Loop/Resources/Sounds/electronic_lgs_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 18566B364CC2520CFBDB2607 /* Loop/Resources/Sounds/electronic_lgs_medium.caf */; }; - 9695396DE1F34E51C8CF0225 /* Loop/Resources/Sounds/electronic_lgs_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = A746D58F0F0815133AC12A9D /* Loop/Resources/Sounds/electronic_lgs_mild.caf */; }; - 06DEBA22295370ED71FA6FF1 /* Loop/Resources/Sounds/electronic_ulg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = EB5FAFC0C4DA8D94009F0EA7 /* Loop/Resources/Sounds/electronic_ulg_harsh.caf */; }; - 4A1C8ED0D43F5DB88D9E54EF /* Loop/Resources/Sounds/electronic_ulg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 6DEA9E9B5623F8F77822D99C /* Loop/Resources/Sounds/electronic_ulg_medium.caf */; }; - B47913E810409CB0B7BC6576 /* Loop/Resources/Sounds/electronic_ulg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 780F7D310A3BFB3C3F22694C /* Loop/Resources/Sounds/electronic_ulg_mild.caf */; }; - 82D4B3B73B633E0CDA8CDF73 /* Loop/Resources/Sounds/glass_bell.caf in Resources */ = {isa = PBXBuildFile; fileRef = 92A703ABE59E0E23337CE6B1 /* Loop/Resources/Sounds/glass_bell.caf */; }; - ACEDEA57D2D24A7AA7010A7B /* Loop/Resources/Sounds/hard_saw_wave.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8032B80CBB012479E89EBDE0 /* Loop/Resources/Sounds/hard_saw_wave.caf */; }; - 908A8AAFECDBC70B57169E3D /* Loop/Resources/Sounds/harp.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8767531B981D2B1C3284D95E /* Loop/Resources/Sounds/harp.caf */; }; - EB2A217C9AD057A7DF01F116 /* Loop/Resources/Sounds/meow.caf in Resources */ = {isa = PBXBuildFile; fileRef = DE9E9CEE9FBE4CDD17605266 /* Loop/Resources/Sounds/meow.caf */; }; - 706AA94D1D4B49F51B04B9D6 /* Loop/Resources/Sounds/open_can.caf in Resources */ = {isa = PBXBuildFile; fileRef = D212086C7473C12CFAE3EDA7 /* Loop/Resources/Sounds/open_can.caf */; }; - D87EFB28C39228F73654A56A /* Loop/Resources/Sounds/open_wrapper.caf in Resources */ = {isa = PBXBuildFile; fileRef = 47B3FD13DA5BEB4A2CBBA86C /* Loop/Resources/Sounds/open_wrapper.caf */; }; - C35102622CBDADC4F8A29531 /* Loop/Resources/Sounds/saw_wave.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8A58B1B290C65AE2DFC96CE6 /* Loop/Resources/Sounds/saw_wave.caf */; }; - 0C63FC60520110C35B54456A /* Loop/Resources/Sounds/sine_wave.caf in Resources */ = {isa = PBXBuildFile; fileRef = 345BE45BC6D5B01FBA7FE0B4 /* Loop/Resources/Sounds/sine_wave.caf */; }; - ACC87746A312EF0DF019CE18 /* Loop/Resources/Sounds/voice_hg_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9028674AC1F9A25E599AB2CD /* Loop/Resources/Sounds/voice_hg_amy.caf */; }; - 29074112CB80B5D0729833D2 /* Loop/Resources/Sounds/voice_hg_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = D1C8C12CAA6CF822846035AA /* Loop/Resources/Sounds/voice_hg_bryce.caf */; }; - B0C24C973471C4B971DF3034 /* Loop/Resources/Sounds/voice_lg_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = D5394844C0EA517BF83DAC67 /* Loop/Resources/Sounds/voice_lg_amy.caf */; }; - E23DB8960A0D78A3D5BBBAC1 /* Loop/Resources/Sounds/voice_lg_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = 15A11683B521C116718CCF03 /* Loop/Resources/Sounds/voice_lg_bryce.caf */; }; - CD753CCE4F90A5DF08CD6FE3 /* Loop/Resources/Sounds/voice_lgs_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B095931D004E1C6D1E2A /* Loop/Resources/Sounds/voice_lgs_amy.caf */; }; - 82F11F858069EDA2DF65D909 /* Loop/Resources/Sounds/voice_lgs_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = FB860882201269230F24C96C /* Loop/Resources/Sounds/voice_lgs_bryce.caf */; }; - 0996021E82CDE7DA7A3DD544 /* Loop/Resources/Sounds/voice_ulg_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = B08A706BF6BC6555453D3E56 /* Loop/Resources/Sounds/voice_ulg_amy.caf */; }; - 8BAF071E821EAC3FB1B183B3 /* Loop/Resources/Sounds/voice_ulg_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = 1F9BFD4BD97793AAE2407DCB /* Loop/Resources/Sounds/voice_ulg_bryce.caf */; }; + 01414522FC033F2B7A9B223A /* crying.caf in Resources */ = {isa = PBXBuildFile; fileRef = 85203550670F2E9BE92F27D8 /* crying.caf */; }; + 0956C3313D5165B71E491FFA /* ComplicationSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCF57BFE5DDB939285839898 /* ComplicationSnapshot.swift */; }; + 0A02E77AD7536C322933D49B /* minimal.caf in Resources */ = {isa = PBXBuildFile; fileRef = 74145BE3C46270883D295A1F /* minimal.caf */; }; + 124B300527D84BE2A1233A91 /* urgent_low.caf in Resources */ = {isa = PBXBuildFile; fileRef = C9EA3F48338B49BD9C6BD0F9 /* urgent_low.caf */; }; 1419606928D9554E00BA86E0 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 437AFEE6203688CF008C4892 /* LoopKitUI.framework */; }; 1419606A28D955BC00BA86E0 /* MockKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C101947127DD473C004E7EB8 /* MockKitUI.framework */; }; 142CB7592A60BF2E0075748A /* EditMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142CB7582A60BF2E0075748A /* EditMode.swift */; }; @@ -66,8 +27,6 @@ 147EFE8E2A8BCC5500272438 /* DefaultAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 147EFE8D2A8BCC5500272438 /* DefaultAssets.xcassets */; }; 147EFE902A8BCD8000272438 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 147EFE8F2A8BCD8000272438 /* DerivedAssets.xcassets */; }; 147EFE922A8BCD8A00272438 /* DerivedAssetsBase.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 147EFE912A8BCD8A00272438 /* DerivedAssetsBase.xcassets */; }; - 1F0000012FBA000100000001 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 1F0000002FBA000100000001 /* AppIcon.icon */; }; - 1F0000022FBA000100000001 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 1F0000002FBA000100000001 /* AppIcon.icon */; }; 1481F9BB28DA26F4004C5AEB /* LoopUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F75288B1DFE1DC600C322D6 /* LoopUI.framework */; }; 149A28BB2A853E5100052EDF /* CarbEntryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 149A28BA2A853E5100052EDF /* CarbEntryViewModel.swift */; }; 149A28BD2A853E6C00052EDF /* CarbEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 149A28BC2A853E6C00052EDF /* CarbEntryView.swift */; }; @@ -101,7 +60,7 @@ 14C970862C5C358C00E8A01B /* FavoriteFoodInsightsChartsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C970852C5C358C00E8A01B /* FavoriteFoodInsightsChartsView.swift */; }; 14D906F42A846510006EB79A /* FavoriteFoodsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14D906F32A846510006EB79A /* FavoriteFoodsViewModel.swift */; }; 14ED83F62C6421F9008B4A5C /* FavoriteFoodInsightsCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14ED83F52C6421F9008B4A5C /* FavoriteFoodInsightsCardView.swift */; }; - 16E5A3BC4D931CE6AED18518 /* Loop/Resources/Sounds/trill.caf in Resources */ = {isa = PBXBuildFile; fileRef = 21F13EB666EBFDD03F99CB7E /* Loop/Resources/Sounds/trill.caf */; }; + 16E5A3BC4D931CE6AED18518 /* trill.caf in Resources */ = {isa = PBXBuildFile; fileRef = 21F13EB666EBFDD03F99CB7E /* trill.caf */; }; 1D05219B2469E9DF000EBBDE /* StoredAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D05219A2469E9DF000EBBDE /* StoredAlert.swift */; }; 1D05219D2469F1F5000EBBDE /* AlertStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D05219C2469F1F5000EBBDE /* AlertStore.swift */; }; 1D080CBD2473214A00356610 /* AlertStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1D080CBB2473214A00356610 /* AlertStore.xcdatamodeld */; }; @@ -129,9 +88,13 @@ 1DDE273E24AEA4B000796622 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE09BA824A3E23F009EE9F9 /* SettingsView.swift */; }; 1DDE274024AEA4F200796622 /* NotificationsCriticalAlertPermissionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA46B5F2492E2E300D71A63 /* NotificationsCriticalAlertPermissionsView.swift */; }; 1DFE9E172447B6270082C280 /* UserNotificationAlertSchedulerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE9E162447B6270082C280 /* UserNotificationAlertSchedulerTests.swift */; }; - 230BE095719B8D8969B32A9C /* Loop/Resources/Sounds/bloop.caf in Resources */ = {isa = PBXBuildFile; fileRef = AF16A5EDEB9DF756890E0C09 /* Loop/Resources/Sounds/bloop.caf */; }; - 2C694BFD5806E573AFAB5E2E /* Loop/Resources/Sounds/dings.caf in Resources */ = {isa = PBXBuildFile; fileRef = 62F7CB5D5DC337E705032AC7 /* Loop/Resources/Sounds/dings.caf */; }; - 33644743F20B3706E3107FAC /* Loop/Resources/Sounds/mood_synth.caf in Resources */ = {isa = PBXBuildFile; fileRef = BEC0BA802BE613F144AC3A70 /* Loop/Resources/Sounds/mood_synth.caf */; }; + 1F0000012FBA000100000001 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 1F0000002FBA000100000001 /* AppIcon.icon */; }; + 1F0000022FBA000100000001 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 1F0000002FBA000100000001 /* AppIcon.icon */; }; + 230BE095719B8D8969B32A9C /* bloop.caf in Resources */ = {isa = PBXBuildFile; fileRef = AF16A5EDEB9DF756890E0C09 /* bloop.caf */; }; + 2615C162995594E8CF144CB2 /* ComplicationSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8AF2728D70D69B00A4F38E /* ComplicationSnapshotTests.swift */; }; + 2C694BFD5806E573AFAB5E2E /* dings.caf in Resources */ = {isa = PBXBuildFile; fileRef = 62F7CB5D5DC337E705032AC7 /* dings.caf */; }; + 33644743F20B3706E3107FAC /* mood_synth.caf in Resources */ = {isa = PBXBuildFile; fileRef = BEC0BA802BE613F144AC3A70 /* mood_synth.caf */; }; + 3E1A1B91ED0E8C8F5663988E /* LoopComplications.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C8C0BEB6A6B9A65E0D56AF /* LoopComplications.swift */; }; 3ED3198A2EB659E600820BCF /* GlucoseLiveActivityConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED319882EB659E600820BCF /* GlucoseLiveActivityConfiguration.swift */; }; 3ED3198B2EB659E600820BCF /* ChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED319872EB659E600820BCF /* ChartView.swift */; }; 3ED3198C2EB659E600820BCF /* BasalViewActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED319862EB659E600820BCF /* BasalViewActivity.swift */; }; @@ -147,6 +110,7 @@ 3ED3199F2EB65AFE00820BCF /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED3199E2EB65AFE00820BCF /* LocalizedString.swift */; }; 3ED319A12EB65B4100820BCF /* Bootstrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED319A02EB65B4100820BCF /* Bootstrap.swift */; }; 3ED319A32EB65DA800820BCF /* LiveActivityManagerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED319A22EB65DA300820BCF /* LiveActivityManagerProxy.swift */; }; + 40ACF455B8FB3B38F2D8C0CA /* ComplicationSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCF57BFE5DDB939285839898 /* ComplicationSnapshot.swift */; }; 43027F0F1DFE0EC900C51989 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F526D5E1DF2459000A04910 /* HKUnit.swift */; }; 4302F4E11D4E9C8900F0FCAF /* TextFieldTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4302F4E01D4E9C8900F0FCAF /* TextFieldTableViewController.swift */; }; 4302F4E31D4EA54200F0FCAF /* InsulinDeliveryTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4302F4E21D4EA54200F0FCAF /* InsulinDeliveryTableViewController.swift */; }; @@ -272,16 +236,13 @@ 4F7E8AC520E2AB9600AEA65E /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F7E8AC420E2AB9600AEA65E /* Date.swift */; }; 4FC8C8011DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC8C8001DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift */; }; 4FDDD23720DC51DF00D04B16 /* LoopDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FDDD23620DC51DF00D04B16 /* LoopDataManager.swift */; }; - D7A3000100000000000A0002 /* WatchCGMHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3000100000000000A0001 /* WatchCGMHost.swift */; }; - D7A3000100000000000A0004 /* WatchDeviceManagers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3000100000000000A0003 /* WatchDeviceManagers.swift */; }; - 101AA7370B0E499FBD88BB9F /* G7SensorKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F8760AF31E3C43F6AFBA1BE7 /* G7SensorKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 921B7FAE002E40EFAB3F50C6 /* G7SensorKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8760AF31E3C43F6AFBA1BE7 /* G7SensorKit.framework */; }; - 8B5CF0144D79477AACBE4A82 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344628320A7A3BE00C4BE6F /* LoopKit.framework */; }; 4FF4D0F81E1725B000846527 /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434F54561D287FDB002A9274 /* NibLoadable.swift */; }; + 59C09A9513767F582B35F7F9 /* Loop Complications.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = AC0853072224AE8CC4C388E7 /* Loop Complications.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 6C089A6A6A8AB8F5D835C9F1 /* ComplicationSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCF57BFE5DDB939285839898 /* ComplicationSnapshot.swift */; }; 5021CA8C264146C9DFF33E9D /* PluginPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE102E23D8C1C1291EF66387 /* PluginPickerView.swift */; }; 7D23667D21250C7E0028B67D /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D23667C21250C7E0028B67D /* LocalizedString.swift */; }; 7E69CFFC2A16A77E00203CBD /* ResetLoopManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E69CFFB2A16A77E00203CBD /* ResetLoopManager.swift */; }; - 824A01CBE97A4F7A9D32F7D6 /* Loop/Resources/Sounds/clear_chimes.caf in Resources */ = {isa = PBXBuildFile; fileRef = 4A645E0E2DDA534A635C9201 /* Loop/Resources/Sounds/clear_chimes.caf */; }; + 824A01CBE97A4F7A9D32F7D6 /* clear_chimes.caf in Resources */ = {isa = PBXBuildFile; fileRef = 4A645E0E2DDA534A635C9201 /* clear_chimes.caf */; }; 841306BB2F7F0D9C00AF0320 /* ReferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841306BA2F7F0D9C00AF0320 /* ReferencesView.swift */; }; 84213C752D932F0F00642E78 /* InsulinDeliveryLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84213C742D932F0F00642E78 /* InsulinDeliveryLog.swift */; }; 84213C892D94941000642E78 /* InsulinDeliveryLogEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84213C882D94941000642E78 /* InsulinDeliveryLogEvent.swift */; }; @@ -377,13 +338,13 @@ 89F9119424358E4500ECCAF3 /* CarbAbsorptionTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F9119324358E4500ECCAF3 /* CarbAbsorptionTime.swift */; }; 89F9119624358E6900ECCAF3 /* BolusPickerValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F9119524358E6900ECCAF3 /* BolusPickerValues.swift */; }; 89FE21AD24AC57E30033F501 /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89FE21AC24AC57E30033F501 /* Collection.swift */; }; - 9960C6964DD51D81093FAD5D /* Loop/Resources/Sounds/chime.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3F7A1D72697082DC3A14587E /* Loop/Resources/Sounds/chime.caf */; }; - 9B8FCBDD6E3A9E603C4AABAD /* Loop/Resources/Sounds/bright_alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = 270F994CD56D566028B3CB25 /* Loop/Resources/Sounds/bright_alarm.caf */; }; + 9960C6964DD51D81093FAD5D /* chime.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3F7A1D72697082DC3A14587E /* chime.caf */; }; + 9B8FCBDD6E3A9E603C4AABAD /* bright_alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = 270F994CD56D566028B3CB25 /* bright_alarm.caf */; }; A1B2C3D40000000000000002 /* GlucoseAlertManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000001 /* GlucoseAlertManagerTests.swift */; }; - A1B2C3D40000000000000012 /* AlarmSoundCatalogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000011 /* AlarmSoundCatalogTests.swift */; }; - A2779286E117BD3627DA0618 /* Loop/Resources/Sounds/simple.caf in Resources */ = {isa = PBXBuildFile; fileRef = 5181B60CBF3CB2DA537F0475 /* Loop/Resources/Sounds/simple.caf */; }; - A413B305CB4C4009DA09BE57 /* Loop/Resources/Sounds/bloom.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8FB2394478FF679F436E8EF1 /* Loop/Resources/Sounds/bloom.caf */; }; - A4574F416AD62A1FCDE36D7D /* Loop/Resources/Sounds/high_chimes.caf in Resources */ = {isa = PBXBuildFile; fileRef = 491F8F0D89972E9D8903CFA7 /* Loop/Resources/Sounds/high_chimes.caf */; }; + A2779286E117BD3627DA0618 /* simple.caf in Resources */ = {isa = PBXBuildFile; fileRef = 5181B60CBF3CB2DA537F0475 /* simple.caf */; }; + A413B305CB4C4009DA09BE57 /* bloom.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8FB2394478FF679F436E8EF1 /* bloom.caf */; }; + A44984BCF1848A652E02CE8C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A198971FAA313AF6CDD1ACDF /* Foundation.framework */; }; + A4574F416AD62A1FCDE36D7D /* high_chimes.caf in Resources */ = {isa = PBXBuildFile; fileRef = 491F8F0D89972E9D8903CFA7 /* high_chimes.caf */; }; A91D2A3F26CF0FF80023B075 /* IconTitleSubtitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91D2A3E26CF0FF80023B075 /* IconTitleSubtitleTableViewCell.swift */; }; A91E4C2124F867A700BE9213 /* StoredAlertTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91E4C2024F867A700BE9213 /* StoredAlertTests.swift */; }; A91E4C2324F86F1000BE9213 /* CriticalEventLogExportManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91E4C2224F86F1000BE9213 /* CriticalEventLogExportManagerTests.swift */; }; @@ -514,7 +475,6 @@ C129BF4A2B2791EE00DF15CB /* TemporaryPresetsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C129BF492B2791EE00DF15CB /* TemporaryPresetsManagerTests.swift */; }; C129D3BF2B8697F100FEA6A9 /* TempBasalRecommendationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C129D3BE2B8697F100FEA6A9 /* TempBasalRecommendationTests.swift */; }; C12D72402E4FBC5F00BD628A /* WatchActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12D723F2E4FBC5D00BD628A /* WatchActionsView.swift */; }; - D7A3000100000000000A0006 /* WatchSensorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3000100000000000A0005 /* WatchSensorView.swift */; }; C13072BA2A76AF31009A7C58 /* live_capture_predicted_glucose.json in Resources */ = {isa = PBXBuildFile; fileRef = C13072B92A76AF31009A7C58 /* live_capture_predicted_glucose.json */; }; C13255D6223E7BE2008AF50C /* BolusProgressTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1F8B1DB223862D500DD66CF /* BolusProgressTableViewCell.xib */; }; C138C6F82C1B8A2C00F08F1A /* GlucoseCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C138C6F72C1B8A2C00F08F1A /* GlucoseCondition.swift */; }; @@ -654,15 +614,14 @@ C9A47B0180AAB1FC572D3EFE /* CriticalAlertAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A47B0175B13A9543390BC5 /* CriticalAlertAudioPlayer.swift */; }; C9A47B0280AAB1FC572D3EFF /* GlucoseAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A47B0275B13A9543390BC6 /* GlucoseAlertManager.swift */; }; C9A47B0380AAB1FC572D3F00 /* GlucoseAlertSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A47B0375B13A9543390BC7 /* GlucoseAlertSettingsView.swift */; }; - C9A47B0480AAB1FC572D3F01 /* Loop/Resources/Sounds/critical.caf in Resources */ = {isa = PBXBuildFile; fileRef = C9A47B0475B13A9543390BC8 /* Loop/Resources/Sounds/critical.caf */; }; + C9A47B0480AAB1FC572D3F01 /* critical.caf in Resources */ = {isa = PBXBuildFile; fileRef = C9A47B0475B13A9543390BC8 /* critical.caf */; }; C9SCENED0000000000000001 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9SCENED0000000000000002 /* SceneDelegate.swift */; }; - CB1AF8155B1761A30ACA5591 /* Loop/Resources/Sounds/synth.caf in Resources */ = {isa = PBXBuildFile; fileRef = 83081FFB0497A4596F597830 /* Loop/Resources/Sounds/synth.caf */; }; - D6E28676C2C7751C77C7F2FB /* Loop/Resources/Sounds/spring.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9471E156FC10D6D0FF219C77 /* Loop/Resources/Sounds/spring.caf */; }; + CB1AF8155B1761A30ACA5591 /* synth.caf in Resources */ = {isa = PBXBuildFile; fileRef = 83081FFB0497A4596F597830 /* synth.caf */; }; + D6E28676C2C7751C77C7F2FB /* spring.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9471E156FC10D6D0FF219C77 /* spring.caf */; }; DD3DBD292A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3DBD282A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift */; }; - A1B2C3D42E8F000100000002 /* GuardAndRunwaySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D42E8F000100000001 /* GuardAndRunwaySelectionView.swift */; }; DDC389FC2A2BC6670066E2E8 /* SettingsView+algorithmExperimentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC389FB2A2BC6670066E2E8 /* SettingsView+algorithmExperimentsSection.swift */; }; DDC389FE2A2C4C830066E2E8 /* GlucoseBasedApplicationFactorSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC389FD2A2C4C830066E2E8 /* GlucoseBasedApplicationFactorSelectionView.swift */; }; - DE965737FE2DAE9FE692769E /* Loop/Resources/Sounds/alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0FC34B523FE5A50C920506D5 /* Loop/Resources/Sounds/alarm.caf */; }; + DE965737FE2DAE9FE692769E /* alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0FC34B523FE5A50C920506D5 /* alarm.caf */; }; E93E865424DB6CBA00FF40C8 /* retrospective_output.json in Resources */ = {isa = PBXBuildFile; fileRef = E93E865324DB6CBA00FF40C8 /* retrospective_output.json */; }; E93E865624DB731900FF40C8 /* predicted_glucose_without_retrospective.json in Resources */ = {isa = PBXBuildFile; fileRef = E93E865524DB731900FF40C8 /* predicted_glucose_without_retrospective.json */; }; E93E865824DB75BE00FF40C8 /* predicted_glucose_very_negative.json in Resources */ = {isa = PBXBuildFile; fileRef = E93E865724DB75BD00FF40C8 /* predicted_glucose_very_negative.json */; }; @@ -707,7 +666,8 @@ E9C58A7E24DB529A00487A17 /* dynamic_glucose_effect_partially_observed.json in Resources */ = {isa = PBXBuildFile; fileRef = E9C58A7924DB529A00487A17 /* dynamic_glucose_effect_partially_observed.json */; }; E9C58A7F24DB529A00487A17 /* counteraction_effect_falling_glucose.json in Resources */ = {isa = PBXBuildFile; fileRef = E9C58A7A24DB529A00487A17 /* counteraction_effect_falling_glucose.json */; }; E9C58A8024DB529A00487A17 /* insulin_effect.json in Resources */ = {isa = PBXBuildFile; fileRef = E9C58A7B24DB529A00487A17 /* insulin_effect.json */; }; - FA37F6EC10FF02D4F985D724 /* Loop/Resources/Sounds/honk.caf in Resources */ = {isa = PBXBuildFile; fileRef = 6D5FCC7207E50ACC682E9E6E /* Loop/Resources/Sounds/honk.caf */; }; + EB125CD137FD31CD1EFAD0E0 /* ComplicationPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6515A16900D12040FF31E279 /* ComplicationPublisher.swift */; }; + FA37F6EC10FF02D4F985D724 /* honk.caf in Resources */ = {isa = PBXBuildFile; fileRef = 6D5FCC7207E50ACC682E9E6E /* honk.caf */; }; FADE0001000000000000DE02 /* DevelopmentBranchAlerter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FADE0001000000000000DE01 /* DevelopmentBranchAlerter.swift */; }; FEEDB0000000000000000000 /* GlucoseStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000000 /* GlucoseStatistics.swift */; }; FEEDB0000000000000000001 /* StatisticsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000001 /* StatisticsViewModel.swift */; }; @@ -717,6 +677,54 @@ FEEDB0000000000000000004 /* Statistics/TimeInRangeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000004 /* Statistics/TimeInRangeBar.swift */; }; 30E4A68B8C914C5E4B245555 /* InsulinDeliveryHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F69CE2EE3714858772FE7A26 /* InsulinDeliveryHistoryTests.swift */; }; FEEDB0000000000000000005 /* GlucoseStatisticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */; }; + 7877942ED674BA37847514AD /* Loop/Resources/Sounds/acoustic_hg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = A9B1F564432BCBF70C0C6CBB /* Loop/Resources/Sounds/acoustic_hg_harsh.caf */; }; + A82DB18B916DC17D2E180D59 /* Loop/Resources/Sounds/acoustic_hg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3481DFC7974E27ABE6AAC4C3 /* Loop/Resources/Sounds/acoustic_hg_medium.caf */; }; + 4A208427654F9A249EC2CFDB /* Loop/Resources/Sounds/acoustic_hg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 1DCC85EA42C8D22C1EC824D8 /* Loop/Resources/Sounds/acoustic_hg_mild.caf */; }; + F44BED105495ABA988BAB05F /* Loop/Resources/Sounds/acoustic_lg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = A5E01C8397D139F93B60CA0A /* Loop/Resources/Sounds/acoustic_lg_harsh.caf */; }; + B0007FE66FB0FE886FACD77D /* Loop/Resources/Sounds/acoustic_lg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = D64AC8FAA2340810ECB85AE9 /* Loop/Resources/Sounds/acoustic_lg_medium.caf */; }; + D0EBF80B1D85F96B156725AA /* Loop/Resources/Sounds/acoustic_lg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 65E7CB20BE6AB6A39AF072A2 /* Loop/Resources/Sounds/acoustic_lg_mild.caf */; }; + 3461DE9DBC0B97D5662244FB /* Loop/Resources/Sounds/acoustic_lgs_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = 688FE8312F66C4C1909FF93F /* Loop/Resources/Sounds/acoustic_lgs_harsh.caf */; }; + F98A16085E9F216CEDDB18DB /* Loop/Resources/Sounds/acoustic_lgs_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = E5D3EF6AEBD5FFAF2ABB187B /* Loop/Resources/Sounds/acoustic_lgs_medium.caf */; }; + 182579BE820905E2B9FCC1B4 /* Loop/Resources/Sounds/acoustic_lgs_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 16BE482D6AF35127A36FD9EB /* Loop/Resources/Sounds/acoustic_lgs_mild.caf */; }; + 6AB8817E7C134FE630492390 /* Loop/Resources/Sounds/acoustic_ulg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = A96BF0E0B93A88D6BCEC2338 /* Loop/Resources/Sounds/acoustic_ulg_harsh.caf */; }; + BAD8333EC083569038F10E88 /* Loop/Resources/Sounds/acoustic_ulg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = CC3E2E706E45342C823D641F /* Loop/Resources/Sounds/acoustic_ulg_medium.caf */; }; + 6E0CBD04FE69B38497EFC6F0 /* Loop/Resources/Sounds/acoustic_ulg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = E38835D52BBD5607D90B9615 /* Loop/Resources/Sounds/acoustic_ulg_mild.caf */; }; + DB1B4A63BB3DE5DF05D56791 /* Loop/Resources/Sounds/electronic_hg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = 2D388DFB60E5960C857807D0 /* Loop/Resources/Sounds/electronic_hg_harsh.caf */; }; + 0EA4FC2C9D24030B0D586F80 /* Loop/Resources/Sounds/electronic_hg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 987F0560C0D00A3F1AC4D006 /* Loop/Resources/Sounds/electronic_hg_medium.caf */; }; + 09789816D7760CF9BAA96F28 /* Loop/Resources/Sounds/electronic_hg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = C8BD9DF4B53E440810325EA6 /* Loop/Resources/Sounds/electronic_hg_mild.caf */; }; + D1530992424BA31F0A44EA27 /* Loop/Resources/Sounds/electronic_lg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = FE620B9DFF719ED07CEEA1B4 /* Loop/Resources/Sounds/electronic_lg_harsh.caf */; }; + 3E46B514B1D95CD39E91724B /* Loop/Resources/Sounds/electronic_lg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0F7A07E85B57B85C219B1244 /* Loop/Resources/Sounds/electronic_lg_medium.caf */; }; + C9489985ABD6FED5A2E5C857 /* Loop/Resources/Sounds/electronic_lg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = C6BD81B55371D7597A3D2BCC /* Loop/Resources/Sounds/electronic_lg_mild.caf */; }; + B63CAEC6D7662917D8EF89A7 /* Loop/Resources/Sounds/electronic_lgs_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = 177CE52DCE2BF26A5B8D4FCF /* Loop/Resources/Sounds/electronic_lgs_harsh.caf */; }; + 746A339E8C3350AB42CC5A16 /* Loop/Resources/Sounds/electronic_lgs_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 18566B364CC2520CFBDB2607 /* Loop/Resources/Sounds/electronic_lgs_medium.caf */; }; + 9695396DE1F34E51C8CF0225 /* Loop/Resources/Sounds/electronic_lgs_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = A746D58F0F0815133AC12A9D /* Loop/Resources/Sounds/electronic_lgs_mild.caf */; }; + 06DEBA22295370ED71FA6FF1 /* Loop/Resources/Sounds/electronic_ulg_harsh.caf in Resources */ = {isa = PBXBuildFile; fileRef = EB5FAFC0C4DA8D94009F0EA7 /* Loop/Resources/Sounds/electronic_ulg_harsh.caf */; }; + 4A1C8ED0D43F5DB88D9E54EF /* Loop/Resources/Sounds/electronic_ulg_medium.caf in Resources */ = {isa = PBXBuildFile; fileRef = 6DEA9E9B5623F8F77822D99C /* Loop/Resources/Sounds/electronic_ulg_medium.caf */; }; + B47913E810409CB0B7BC6576 /* Loop/Resources/Sounds/electronic_ulg_mild.caf in Resources */ = {isa = PBXBuildFile; fileRef = 780F7D310A3BFB3C3F22694C /* Loop/Resources/Sounds/electronic_ulg_mild.caf */; }; + 82D4B3B73B633E0CDA8CDF73 /* Loop/Resources/Sounds/glass_bell.caf in Resources */ = {isa = PBXBuildFile; fileRef = 92A703ABE59E0E23337CE6B1 /* Loop/Resources/Sounds/glass_bell.caf */; }; + ACEDEA57D2D24A7AA7010A7B /* Loop/Resources/Sounds/hard_saw_wave.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8032B80CBB012479E89EBDE0 /* Loop/Resources/Sounds/hard_saw_wave.caf */; }; + 908A8AAFECDBC70B57169E3D /* Loop/Resources/Sounds/harp.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8767531B981D2B1C3284D95E /* Loop/Resources/Sounds/harp.caf */; }; + EB2A217C9AD057A7DF01F116 /* Loop/Resources/Sounds/meow.caf in Resources */ = {isa = PBXBuildFile; fileRef = DE9E9CEE9FBE4CDD17605266 /* Loop/Resources/Sounds/meow.caf */; }; + 706AA94D1D4B49F51B04B9D6 /* Loop/Resources/Sounds/open_can.caf in Resources */ = {isa = PBXBuildFile; fileRef = D212086C7473C12CFAE3EDA7 /* Loop/Resources/Sounds/open_can.caf */; }; + D87EFB28C39228F73654A56A /* Loop/Resources/Sounds/open_wrapper.caf in Resources */ = {isa = PBXBuildFile; fileRef = 47B3FD13DA5BEB4A2CBBA86C /* Loop/Resources/Sounds/open_wrapper.caf */; }; + C35102622CBDADC4F8A29531 /* Loop/Resources/Sounds/saw_wave.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8A58B1B290C65AE2DFC96CE6 /* Loop/Resources/Sounds/saw_wave.caf */; }; + 0C63FC60520110C35B54456A /* Loop/Resources/Sounds/sine_wave.caf in Resources */ = {isa = PBXBuildFile; fileRef = 345BE45BC6D5B01FBA7FE0B4 /* Loop/Resources/Sounds/sine_wave.caf */; }; + ACC87746A312EF0DF019CE18 /* Loop/Resources/Sounds/voice_hg_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9028674AC1F9A25E599AB2CD /* Loop/Resources/Sounds/voice_hg_amy.caf */; }; + 29074112CB80B5D0729833D2 /* Loop/Resources/Sounds/voice_hg_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = D1C8C12CAA6CF822846035AA /* Loop/Resources/Sounds/voice_hg_bryce.caf */; }; + B0C24C973471C4B971DF3034 /* Loop/Resources/Sounds/voice_lg_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = D5394844C0EA517BF83DAC67 /* Loop/Resources/Sounds/voice_lg_amy.caf */; }; + E23DB8960A0D78A3D5BBBAC1 /* Loop/Resources/Sounds/voice_lg_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = 15A11683B521C116718CCF03 /* Loop/Resources/Sounds/voice_lg_bryce.caf */; }; + CD753CCE4F90A5DF08CD6FE3 /* Loop/Resources/Sounds/voice_lgs_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B095931D004E1C6D1E2A /* Loop/Resources/Sounds/voice_lgs_amy.caf */; }; + 82F11F858069EDA2DF65D909 /* Loop/Resources/Sounds/voice_lgs_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = FB860882201269230F24C96C /* Loop/Resources/Sounds/voice_lgs_bryce.caf */; }; + 0996021E82CDE7DA7A3DD544 /* Loop/Resources/Sounds/voice_ulg_amy.caf in Resources */ = {isa = PBXBuildFile; fileRef = B08A706BF6BC6555453D3E56 /* Loop/Resources/Sounds/voice_ulg_amy.caf */; }; + 8BAF071E821EAC3FB1B183B3 /* Loop/Resources/Sounds/voice_ulg_bryce.caf in Resources */ = {isa = PBXBuildFile; fileRef = 1F9BFD4BD97793AAE2407DCB /* Loop/Resources/Sounds/voice_ulg_bryce.caf */; }; + D7A3000100000000000A0002 /* WatchCGMHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3000100000000000A0001 /* WatchCGMHost.swift */; }; + D7A3000100000000000A0004 /* WatchDeviceManagers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3000100000000000A0003 /* WatchDeviceManagers.swift */; }; + 101AA7370B0E499FBD88BB9F /* G7SensorKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F8760AF31E3C43F6AFBA1BE7 /* G7SensorKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 921B7FAE002E40EFAB3F50C6 /* G7SensorKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8760AF31E3C43F6AFBA1BE7 /* G7SensorKit.framework */; }; + 8B5CF0144D79477AACBE4A82 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344628320A7A3BE00C4BE6F /* LoopKit.framework */; }; + A1B2C3D40000000000000012 /* AlarmSoundCatalogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000011 /* AlarmSoundCatalogTests.swift */; }; + D7A3000100000000000A0006 /* WatchSensorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3000100000000000A0005 /* WatchSensorView.swift */; }; + A1B2C3D42E8F000100000002 /* GuardAndRunwaySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D42E8F000100000001 /* GuardAndRunwaySelectionView.swift */; }; 17EA66859884C0208DED47CF /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; 1C99EE4A3BB181BB23DEF066 /* LoopAlgorithm in Frameworks */ = {isa = PBXBuildFile; productRef = C42F6B8A2B1B9E0142078BF9 /* LoopAlgorithm */; }; BD37CA2F10445DB600A35199 /* TemporaryScheduleOverrideAlgorithmTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */; }; @@ -743,6 +751,13 @@ remoteGlobalIDString = 14B1735B28AED9EC006CCD7C; remoteInfo = SmallStatusWidgetExtension; }; + 1B21307ABB67B1D1DACE2D25 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 43776F841B8022E90074EA36 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 27B118E7F17401410F70FACA; + remoteInfo = "Loop Complications"; + }; 43A943921B926B7B0051FA24 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 43776F841B8022E90074EA36 /* Project object */; @@ -861,10 +876,21 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + ECA50E3AF74E45D0C77CA79C /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 59C09A9513767F582B35F7F9 /* Loop Complications.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0FC34B523FE5A50C920506D5 /* Loop/Resources/Sounds/alarm.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/alarm.caf; sourceTree = SOURCE_ROOT; }; + 0FC34B523FE5A50C920506D5 /* alarm.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/alarm.caf; sourceTree = SOURCE_ROOT; }; 142CB7582A60BF2E0075748A /* EditMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditMode.swift; sourceTree = ""; }; 142CB75A2A60BFC30075748A /* FavoriteFoodsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FavoriteFoodsView.swift; sourceTree = ""; }; 1452F4A82A851C9400F8B9E4 /* FavoriteFoodAddEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteFoodAddEditViewModel.swift; sourceTree = ""; }; @@ -877,7 +903,6 @@ 147EFE8D2A8BCC5500272438 /* DefaultAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DefaultAssets.xcassets; sourceTree = ""; }; 147EFE8F2A8BCD8000272438 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = ""; }; 147EFE912A8BCD8A00272438 /* DerivedAssetsBase.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssetsBase.xcassets; sourceTree = ""; }; - 1F0000002FBA000100000001 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = ""; }; 149A28BA2A853E5100052EDF /* CarbEntryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEntryViewModel.swift; sourceTree = ""; }; 149A28BC2A853E6C00052EDF /* CarbEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEntryView.swift; sourceTree = ""; }; 149A28E32A8A63A700052EDF /* FavoriteFoodDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FavoriteFoodDetailView.swift; sourceTree = ""; }; @@ -926,8 +951,10 @@ 1DC63E7325351BDF004605DA /* TrueTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TrueTime.framework; path = Carthage/Build/iOS/TrueTime.framework; sourceTree = ""; }; 1DE09BA824A3E23F009EE9F9 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 1DFE9E162447B6270082C280 /* UserNotificationAlertSchedulerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationAlertSchedulerTests.swift; sourceTree = ""; }; - 21F13EB666EBFDD03F99CB7E /* Loop/Resources/Sounds/trill.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/trill.caf; sourceTree = SOURCE_ROOT; }; - 270F994CD56D566028B3CB25 /* Loop/Resources/Sounds/bright_alarm.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/bright_alarm.caf; sourceTree = SOURCE_ROOT; }; + 1F0000002FBA000100000001 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = ""; }; + 21F13EB666EBFDD03F99CB7E /* trill.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/trill.caf; sourceTree = SOURCE_ROOT; }; + 270F994CD56D566028B3CB25 /* bright_alarm.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/bright_alarm.caf; sourceTree = SOURCE_ROOT; }; + 2CA0E8DC01120F1A9593061C /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3ED319862EB659E600820BCF /* BasalViewActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasalViewActivity.swift; sourceTree = ""; }; 3ED319872EB659E600820BCF /* ChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartView.swift; sourceTree = ""; }; 3ED319882EB659E600820BCF /* GlucoseLiveActivityConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseLiveActivityConfiguration.swift; sourceTree = ""; }; @@ -941,7 +968,7 @@ 3ED3199E2EB65AFE00820BCF /* LocalizedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedString.swift; sourceTree = ""; }; 3ED319A02EB65B4100820BCF /* Bootstrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bootstrap.swift; sourceTree = ""; }; 3ED319A22EB65DA300820BCF /* LiveActivityManagerProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityManagerProxy.swift; sourceTree = ""; }; - 3F7A1D72697082DC3A14587E /* Loop/Resources/Sounds/chime.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/chime.caf; sourceTree = SOURCE_ROOT; }; + 3F7A1D72697082DC3A14587E /* chime.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/chime.caf; sourceTree = SOURCE_ROOT; }; 4302F4E01D4E9C8900F0FCAF /* TextFieldTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldTableViewController.swift; sourceTree = ""; }; 4302F4E21D4EA54200F0FCAF /* InsulinDeliveryTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsulinDeliveryTableViewController.swift; sourceTree = ""; }; 430B29892041F54A00BA9F93 /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; @@ -1075,8 +1102,8 @@ 43F89CA222BDFBBC006BB54E /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 43FCEEA8221A615B0013DD30 /* StatusChartsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusChartsManager.swift; sourceTree = ""; }; 43FCEEAC221A66780013DD30 /* DateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatter.swift; sourceTree = ""; }; - 491F8F0D89972E9D8903CFA7 /* Loop/Resources/Sounds/high_chimes.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/high_chimes.caf; sourceTree = SOURCE_ROOT; }; - 4A645E0E2DDA534A635C9201 /* Loop/Resources/Sounds/clear_chimes.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/clear_chimes.caf; sourceTree = SOURCE_ROOT; }; + 491F8F0D89972E9D8903CFA7 /* high_chimes.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/high_chimes.caf; sourceTree = SOURCE_ROOT; }; + 4A645E0E2DDA534A635C9201 /* clear_chimes.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/clear_chimes.caf; sourceTree = SOURCE_ROOT; }; 4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = G4ShareSpy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4F08DE8E1E7BB871006741EA /* CollectionType+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CollectionType+Loop.swift"; sourceTree = ""; }; 4F11D3BF20DCBEEC006E072C /* GlucoseBackfillRequestUserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseBackfillRequestUserInfo.swift; sourceTree = ""; }; @@ -1099,14 +1126,13 @@ 4F7E8AC620E2AC0300AEA65E /* WatchPredictedGlucose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchPredictedGlucose.swift; sourceTree = ""; }; 4FC8C8001DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSUserDefaults+StatusExtension.swift"; sourceTree = ""; }; 4FDDD23620DC51DF00D04B16 /* LoopDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopDataManager.swift; sourceTree = ""; }; - D7A3000100000000000A0001 /* WatchCGMHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchCGMHost.swift; sourceTree = ""; }; - D7A3000100000000000A0003 /* WatchDeviceManagers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchDeviceManagers.swift; sourceTree = ""; }; - F8760AF31E3C43F6AFBA1BE7 /* G7SensorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = G7SensorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4FF4D0FF1E18374700846527 /* WatchContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchContext.swift; sourceTree = ""; }; - 5181B60CBF3CB2DA537F0475 /* Loop/Resources/Sounds/simple.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/simple.caf; sourceTree = SOURCE_ROOT; }; - 62F7CB5D5DC337E705032AC7 /* Loop/Resources/Sounds/dings.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/dings.caf; sourceTree = SOURCE_ROOT; }; - 6D5FCC7207E50ACC682E9E6E /* Loop/Resources/Sounds/honk.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/honk.caf; sourceTree = SOURCE_ROOT; }; - 74145BE3C46270883D295A1F /* Loop/Resources/Sounds/minimal.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/minimal.caf; sourceTree = SOURCE_ROOT; }; + 5181B60CBF3CB2DA537F0475 /* simple.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/simple.caf; sourceTree = SOURCE_ROOT; }; + 62F7CB5D5DC337E705032AC7 /* dings.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/dings.caf; sourceTree = SOURCE_ROOT; }; + 6515A16900D12040FF31E279 /* ComplicationPublisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ComplicationPublisher.swift; sourceTree = ""; }; + 6D5FCC7207E50ACC682E9E6E /* honk.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/honk.caf; sourceTree = SOURCE_ROOT; }; + 74145BE3C46270883D295A1F /* minimal.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/minimal.caf; sourceTree = SOURCE_ROOT; }; + 7575B76A6837658308376B90 /* LoopComplications.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = LoopComplications.entitlements; sourceTree = ""; }; 7D23667C21250C7E0028B67D /* LocalizedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LocalizedString.swift; path = LoopUI/Common/LocalizedString.swift; sourceTree = SOURCE_ROOT; }; 7D9BEF132335EC4B005DCFD6 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Intents.strings; sourceTree = ""; }; 7D9BEF292335EC58005DCFD6 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Intents.strings"; sourceTree = ""; }; @@ -1116,7 +1142,7 @@ 7D9BEF812335EC8B005DCFD6 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Intents.strings; sourceTree = ""; }; 7D9BF13A23370E8B005DCFD6 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Intents.strings; sourceTree = ""; }; 7E69CFFB2A16A77E00203CBD /* ResetLoopManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetLoopManager.swift; sourceTree = ""; }; - 83081FFB0497A4596F597830 /* Loop/Resources/Sounds/synth.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/synth.caf; sourceTree = SOURCE_ROOT; }; + 83081FFB0497A4596F597830 /* synth.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/synth.caf; sourceTree = SOURCE_ROOT; }; 840A2F0D2C0F978E003D5E90 /* LoopKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 841306BA2F7F0D9C00AF0320 /* ReferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferencesView.swift; sourceTree = ""; }; 84213C742D932F0F00642E78 /* InsulinDeliveryLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsulinDeliveryLog.swift; sourceTree = ""; }; @@ -1160,7 +1186,7 @@ 84EC162D2C9115CA00D220C5 /* DIYLoopUnitTestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = DIYLoopUnitTestPlan.xctestplan; sourceTree = ""; }; 84F20DFC2D0B9C3A0089DF02 /* EditPresetDurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditPresetDurationView.swift; sourceTree = ""; }; 84FA9D322CF7FD0D004162B4 /* PresetsHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetsHistoryView.swift; sourceTree = ""; }; - 85203550670F2E9BE92F27D8 /* Loop/Resources/Sounds/crying.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/crying.caf; sourceTree = SOURCE_ROOT; }; + 85203550670F2E9BE92F27D8 /* crying.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/crying.caf; sourceTree = SOURCE_ROOT; }; 891B508424342BE1005DA578 /* CarbAndBolusFlowViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbAndBolusFlowViewModel.swift; sourceTree = ""; }; 892A5D29222EF60A008961AB /* MockKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = MockKit.framework; path = Carthage/Build/iOS/MockKit.framework; sourceTree = SOURCE_ROOT; }; 892A5D2B222EF60A008961AB /* MockKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = MockKitUI.framework; path = Carthage/Build/iOS/MockKitUI.framework; sourceTree = SOURCE_ROOT; }; @@ -1218,10 +1244,11 @@ 89F9119324358E4500ECCAF3 /* CarbAbsorptionTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbAbsorptionTime.swift; sourceTree = ""; }; 89F9119524358E6900ECCAF3 /* BolusPickerValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusPickerValues.swift; sourceTree = ""; }; 89FE21AC24AC57E30033F501 /* Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = ""; }; - 8FB2394478FF679F436E8EF1 /* Loop/Resources/Sounds/bloom.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/bloom.caf; sourceTree = SOURCE_ROOT; }; - 9471E156FC10D6D0FF219C77 /* Loop/Resources/Sounds/spring.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/spring.caf; sourceTree = SOURCE_ROOT; }; + 8FB2394478FF679F436E8EF1 /* bloom.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/bloom.caf; sourceTree = SOURCE_ROOT; }; + 9471E156FC10D6D0FF219C77 /* spring.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/spring.caf; sourceTree = SOURCE_ROOT; }; + 9F8AF2728D70D69B00A4F38E /* ComplicationSnapshotTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ComplicationSnapshotTests.swift; sourceTree = ""; }; + A198971FAA313AF6CDD1ACDF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; A1B2C3D40000000000000001 /* GlucoseAlertManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseAlertManagerTests.swift; sourceTree = ""; }; - A1B2C3D40000000000000011 /* AlarmSoundCatalogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmSoundCatalogTests.swift; sourceTree = ""; }; A900531A28D60862000BC15B /* Loop.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop.shortcut; sourceTree = ""; }; A900531B28D608CA000BC15B /* Cancel Override.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Cancel Override.shortcut"; sourceTree = ""; }; A900531C28D6090D000BC15B /* Loop Remote Overrides.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Loop Remote Overrides.shortcut"; sourceTree = ""; }; @@ -1272,6 +1299,7 @@ A9F703742489C9A000C98AD8 /* GlucoseStore+SimulatedCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStore+SimulatedCoreData.swift"; sourceTree = ""; }; A9F703762489D8AA00C98AD8 /* PersistentDeviceLog+SimulatedCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PersistentDeviceLog+SimulatedCoreData.swift"; sourceTree = ""; }; A9FB75F0252BE320004C7D3F /* BolusDosingDecision.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusDosingDecision.swift; sourceTree = ""; }; + AC0853072224AE8CC4C388E7 /* Loop Complications.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Loop Complications.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 8378B7D1F532A5F47CEBB6AE /* CarbEntryChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEntryChange.swift; sourceTree = ""; }; AF16A5EDEB9DF756890E0C09 /* Loop/Resources/Sounds/bloop.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/bloop.caf; sourceTree = SOURCE_ROOT; }; B4001CED28CBBC82002FB414 /* AlertManagementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertManagementView.swift; sourceTree = ""; }; @@ -1332,7 +1360,7 @@ B66D1F422E6A5D6600471149 /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/Main.xcstrings; sourceTree = ""; }; B6F22EF72E95A03800CCA05F /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Intents.strings; sourceTree = ""; }; B6F22EF92E95A03C00CCA05F /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Intents.strings; sourceTree = ""; }; - BEC0BA802BE613F144AC3A70 /* Loop/Resources/Sounds/mood_synth.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/mood_synth.caf; sourceTree = SOURCE_ROOT; }; + BEC0BA802BE613F144AC3A70 /* mood_synth.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/mood_synth.caf; sourceTree = SOURCE_ROOT; }; C1004DEF2981F5B700B8CF94 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; C1004DFD2981F67A00B8CF94 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; C1004E052981F6A100B8CF94 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -1378,7 +1406,6 @@ C12CB9B623106A6200F84978 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Intents.strings; sourceTree = ""; }; C12CB9B823106A6300F84978 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Intents.strings; sourceTree = ""; }; C12D723F2E4FBC5D00BD628A /* WatchActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchActionsView.swift; sourceTree = ""; }; - D7A3000100000000000A0005 /* WatchSensorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSensorView.swift; sourceTree = ""; }; C13072B92A76AF31009A7C58 /* live_capture_predicted_glucose.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = live_capture_predicted_glucose.json; sourceTree = ""; }; C138C6F72C1B8A2C00F08F1A /* GlucoseCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseCondition.swift; sourceTree = ""; }; C13DA2AF24F6C7690098BB29 /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; @@ -1489,55 +1516,15 @@ C1FAD5182E7E0C3100F7FAD9 /* ChartPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPageView.swift; sourceTree = ""; }; C1FB428B217806A300FAB378 /* StateColorPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateColorPalette.swift; sourceTree = ""; }; C1FB428E217921D600FAB378 /* PumpManagerUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpManagerUI.swift; sourceTree = ""; }; + C535A805BFAB88306874F9E2 /* WatchApp.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = WatchApp.entitlements; sourceTree = ""; }; C9A47B0175B13A9543390BC5 /* CriticalAlertAudioPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CriticalAlertAudioPlayer.swift; sourceTree = ""; }; C9A47B0275B13A9543390BC6 /* GlucoseAlertManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseAlertManager.swift; sourceTree = ""; }; C9A47B0375B13A9543390BC7 /* GlucoseAlertSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseAlertSettingsView.swift; sourceTree = ""; }; - C9A47B0475B13A9543390BC8 /* Loop/Resources/Sounds/critical.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/critical.caf; sourceTree = SOURCE_ROOT; }; - C9EA3F48338B49BD9C6BD0F9 /* Loop/Resources/Sounds/urgent_low.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/urgent_low.caf; sourceTree = SOURCE_ROOT; }; - A9B1F564432BCBF70C0C6CBB /* Loop/Resources/Sounds/acoustic_hg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_hg_harsh.caf; sourceTree = SOURCE_ROOT; }; - 3481DFC7974E27ABE6AAC4C3 /* Loop/Resources/Sounds/acoustic_hg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_hg_medium.caf; sourceTree = SOURCE_ROOT; }; - 1DCC85EA42C8D22C1EC824D8 /* Loop/Resources/Sounds/acoustic_hg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_hg_mild.caf; sourceTree = SOURCE_ROOT; }; - A5E01C8397D139F93B60CA0A /* Loop/Resources/Sounds/acoustic_lg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lg_harsh.caf; sourceTree = SOURCE_ROOT; }; - D64AC8FAA2340810ECB85AE9 /* Loop/Resources/Sounds/acoustic_lg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lg_medium.caf; sourceTree = SOURCE_ROOT; }; - 65E7CB20BE6AB6A39AF072A2 /* Loop/Resources/Sounds/acoustic_lg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lg_mild.caf; sourceTree = SOURCE_ROOT; }; - 688FE8312F66C4C1909FF93F /* Loop/Resources/Sounds/acoustic_lgs_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lgs_harsh.caf; sourceTree = SOURCE_ROOT; }; - E5D3EF6AEBD5FFAF2ABB187B /* Loop/Resources/Sounds/acoustic_lgs_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lgs_medium.caf; sourceTree = SOURCE_ROOT; }; - 16BE482D6AF35127A36FD9EB /* Loop/Resources/Sounds/acoustic_lgs_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lgs_mild.caf; sourceTree = SOURCE_ROOT; }; - A96BF0E0B93A88D6BCEC2338 /* Loop/Resources/Sounds/acoustic_ulg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_ulg_harsh.caf; sourceTree = SOURCE_ROOT; }; - CC3E2E706E45342C823D641F /* Loop/Resources/Sounds/acoustic_ulg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_ulg_medium.caf; sourceTree = SOURCE_ROOT; }; - E38835D52BBD5607D90B9615 /* Loop/Resources/Sounds/acoustic_ulg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_ulg_mild.caf; sourceTree = SOURCE_ROOT; }; - 2D388DFB60E5960C857807D0 /* Loop/Resources/Sounds/electronic_hg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_hg_harsh.caf; sourceTree = SOURCE_ROOT; }; - 987F0560C0D00A3F1AC4D006 /* Loop/Resources/Sounds/electronic_hg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_hg_medium.caf; sourceTree = SOURCE_ROOT; }; - C8BD9DF4B53E440810325EA6 /* Loop/Resources/Sounds/electronic_hg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_hg_mild.caf; sourceTree = SOURCE_ROOT; }; - FE620B9DFF719ED07CEEA1B4 /* Loop/Resources/Sounds/electronic_lg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lg_harsh.caf; sourceTree = SOURCE_ROOT; }; - 0F7A07E85B57B85C219B1244 /* Loop/Resources/Sounds/electronic_lg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lg_medium.caf; sourceTree = SOURCE_ROOT; }; - C6BD81B55371D7597A3D2BCC /* Loop/Resources/Sounds/electronic_lg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lg_mild.caf; sourceTree = SOURCE_ROOT; }; - 177CE52DCE2BF26A5B8D4FCF /* Loop/Resources/Sounds/electronic_lgs_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lgs_harsh.caf; sourceTree = SOURCE_ROOT; }; - 18566B364CC2520CFBDB2607 /* Loop/Resources/Sounds/electronic_lgs_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lgs_medium.caf; sourceTree = SOURCE_ROOT; }; - A746D58F0F0815133AC12A9D /* Loop/Resources/Sounds/electronic_lgs_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lgs_mild.caf; sourceTree = SOURCE_ROOT; }; - EB5FAFC0C4DA8D94009F0EA7 /* Loop/Resources/Sounds/electronic_ulg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_ulg_harsh.caf; sourceTree = SOURCE_ROOT; }; - 6DEA9E9B5623F8F77822D99C /* Loop/Resources/Sounds/electronic_ulg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_ulg_medium.caf; sourceTree = SOURCE_ROOT; }; - 780F7D310A3BFB3C3F22694C /* Loop/Resources/Sounds/electronic_ulg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_ulg_mild.caf; sourceTree = SOURCE_ROOT; }; - 92A703ABE59E0E23337CE6B1 /* Loop/Resources/Sounds/glass_bell.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/glass_bell.caf; sourceTree = SOURCE_ROOT; }; - 8032B80CBB012479E89EBDE0 /* Loop/Resources/Sounds/hard_saw_wave.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/hard_saw_wave.caf; sourceTree = SOURCE_ROOT; }; - 8767531B981D2B1C3284D95E /* Loop/Resources/Sounds/harp.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/harp.caf; sourceTree = SOURCE_ROOT; }; - DE9E9CEE9FBE4CDD17605266 /* Loop/Resources/Sounds/meow.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/meow.caf; sourceTree = SOURCE_ROOT; }; - D212086C7473C12CFAE3EDA7 /* Loop/Resources/Sounds/open_can.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/open_can.caf; sourceTree = SOURCE_ROOT; }; - 47B3FD13DA5BEB4A2CBBA86C /* Loop/Resources/Sounds/open_wrapper.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/open_wrapper.caf; sourceTree = SOURCE_ROOT; }; - 8A58B1B290C65AE2DFC96CE6 /* Loop/Resources/Sounds/saw_wave.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/saw_wave.caf; sourceTree = SOURCE_ROOT; }; - 345BE45BC6D5B01FBA7FE0B4 /* Loop/Resources/Sounds/sine_wave.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/sine_wave.caf; sourceTree = SOURCE_ROOT; }; - 9028674AC1F9A25E599AB2CD /* Loop/Resources/Sounds/voice_hg_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_hg_amy.caf; sourceTree = SOURCE_ROOT; }; - D1C8C12CAA6CF822846035AA /* Loop/Resources/Sounds/voice_hg_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_hg_bryce.caf; sourceTree = SOURCE_ROOT; }; - D5394844C0EA517BF83DAC67 /* Loop/Resources/Sounds/voice_lg_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lg_amy.caf; sourceTree = SOURCE_ROOT; }; - 15A11683B521C116718CCF03 /* Loop/Resources/Sounds/voice_lg_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lg_bryce.caf; sourceTree = SOURCE_ROOT; }; - 34C6B095931D004E1C6D1E2A /* Loop/Resources/Sounds/voice_lgs_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lgs_amy.caf; sourceTree = SOURCE_ROOT; }; - FB860882201269230F24C96C /* Loop/Resources/Sounds/voice_lgs_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lgs_bryce.caf; sourceTree = SOURCE_ROOT; }; - B08A706BF6BC6555453D3E56 /* Loop/Resources/Sounds/voice_ulg_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_ulg_amy.caf; sourceTree = SOURCE_ROOT; }; - 1F9BFD4BD97793AAE2407DCB /* Loop/Resources/Sounds/voice_ulg_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_ulg_bryce.caf; sourceTree = SOURCE_ROOT; }; + C9A47B0475B13A9543390BC8 /* critical.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/critical.caf; sourceTree = SOURCE_ROOT; }; + C9EA3F48338B49BD9C6BD0F9 /* urgent_low.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/urgent_low.caf; sourceTree = SOURCE_ROOT; }; C9SCENED0000000000000002 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; CE102E23D8C1C1291EF66387 /* PluginPickerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PluginPickerView.swift; sourceTree = ""; }; DD3DBD282A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegralRetrospectiveCorrectionSelectionView.swift; sourceTree = ""; }; - A1B2C3D42E8F000100000001 /* GuardAndRunwaySelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardAndRunwaySelectionView.swift; sourceTree = ""; }; DDC389FB2A2BC6670066E2E8 /* SettingsView+algorithmExperimentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+algorithmExperimentsSection.swift"; sourceTree = ""; }; DDC389FD2A2C4C830066E2E8 /* GlucoseBasedApplicationFactorSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseBasedApplicationFactorSelectionView.swift; sourceTree = ""; }; E4854073AAE5C2CB97167F63 /* HealthAccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthAccessView.swift; sourceTree = ""; }; @@ -1584,7 +1571,9 @@ E9C58A7B24DB529A00487A17 /* insulin_effect.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = insulin_effect.json; sourceTree = ""; }; F5D9C01727DABBE0002E48F6 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Intents.strings; sourceTree = ""; }; F5E0BDD327E1D71C0033557E /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Intents.strings; sourceTree = ""; }; + F9C8C0BEB6A6B9A65E0D56AF /* LoopComplications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoopComplications.swift; sourceTree = ""; }; FADE0001000000000000DE01 /* DevelopmentBranchAlerter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevelopmentBranchAlerter.swift; sourceTree = ""; }; + FCF57BFE5DDB939285839898 /* ComplicationSnapshot.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ComplicationSnapshot.swift; sourceTree = ""; }; FEEDF0000000000000000000 /* GlucoseStatistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseStatistics.swift; sourceTree = ""; }; FEEDF0000000000000000001 /* StatisticsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsViewModel.swift; sourceTree = ""; }; FEEDF0000000000000000002 /* Statistics/StatisticsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics/StatisticsView.swift; sourceTree = ""; }; @@ -1593,6 +1582,52 @@ FEEDF0000000000000000004 /* Statistics/TimeInRangeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics/TimeInRangeBar.swift; sourceTree = ""; }; F69CE2EE3714858772FE7A26 /* InsulinDeliveryHistoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsulinDeliveryHistoryTests.swift; sourceTree = ""; }; FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseStatisticsTests.swift; sourceTree = ""; }; + D7A3000100000000000A0001 /* WatchCGMHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchCGMHost.swift; sourceTree = ""; }; + D7A3000100000000000A0003 /* WatchDeviceManagers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchDeviceManagers.swift; sourceTree = ""; }; + F8760AF31E3C43F6AFBA1BE7 /* G7SensorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = G7SensorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A1B2C3D40000000000000011 /* AlarmSoundCatalogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmSoundCatalogTests.swift; sourceTree = ""; }; + D7A3000100000000000A0005 /* WatchSensorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSensorView.swift; sourceTree = ""; }; + A9B1F564432BCBF70C0C6CBB /* Loop/Resources/Sounds/acoustic_hg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_hg_harsh.caf; sourceTree = SOURCE_ROOT; }; + 3481DFC7974E27ABE6AAC4C3 /* Loop/Resources/Sounds/acoustic_hg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_hg_medium.caf; sourceTree = SOURCE_ROOT; }; + 1DCC85EA42C8D22C1EC824D8 /* Loop/Resources/Sounds/acoustic_hg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_hg_mild.caf; sourceTree = SOURCE_ROOT; }; + A5E01C8397D139F93B60CA0A /* Loop/Resources/Sounds/acoustic_lg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lg_harsh.caf; sourceTree = SOURCE_ROOT; }; + D64AC8FAA2340810ECB85AE9 /* Loop/Resources/Sounds/acoustic_lg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lg_medium.caf; sourceTree = SOURCE_ROOT; }; + 65E7CB20BE6AB6A39AF072A2 /* Loop/Resources/Sounds/acoustic_lg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lg_mild.caf; sourceTree = SOURCE_ROOT; }; + 688FE8312F66C4C1909FF93F /* Loop/Resources/Sounds/acoustic_lgs_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lgs_harsh.caf; sourceTree = SOURCE_ROOT; }; + E5D3EF6AEBD5FFAF2ABB187B /* Loop/Resources/Sounds/acoustic_lgs_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lgs_medium.caf; sourceTree = SOURCE_ROOT; }; + 16BE482D6AF35127A36FD9EB /* Loop/Resources/Sounds/acoustic_lgs_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_lgs_mild.caf; sourceTree = SOURCE_ROOT; }; + A96BF0E0B93A88D6BCEC2338 /* Loop/Resources/Sounds/acoustic_ulg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_ulg_harsh.caf; sourceTree = SOURCE_ROOT; }; + CC3E2E706E45342C823D641F /* Loop/Resources/Sounds/acoustic_ulg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_ulg_medium.caf; sourceTree = SOURCE_ROOT; }; + E38835D52BBD5607D90B9615 /* Loop/Resources/Sounds/acoustic_ulg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/acoustic_ulg_mild.caf; sourceTree = SOURCE_ROOT; }; + 2D388DFB60E5960C857807D0 /* Loop/Resources/Sounds/electronic_hg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_hg_harsh.caf; sourceTree = SOURCE_ROOT; }; + 987F0560C0D00A3F1AC4D006 /* Loop/Resources/Sounds/electronic_hg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_hg_medium.caf; sourceTree = SOURCE_ROOT; }; + C8BD9DF4B53E440810325EA6 /* Loop/Resources/Sounds/electronic_hg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_hg_mild.caf; sourceTree = SOURCE_ROOT; }; + FE620B9DFF719ED07CEEA1B4 /* Loop/Resources/Sounds/electronic_lg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lg_harsh.caf; sourceTree = SOURCE_ROOT; }; + 0F7A07E85B57B85C219B1244 /* Loop/Resources/Sounds/electronic_lg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lg_medium.caf; sourceTree = SOURCE_ROOT; }; + C6BD81B55371D7597A3D2BCC /* Loop/Resources/Sounds/electronic_lg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lg_mild.caf; sourceTree = SOURCE_ROOT; }; + 177CE52DCE2BF26A5B8D4FCF /* Loop/Resources/Sounds/electronic_lgs_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lgs_harsh.caf; sourceTree = SOURCE_ROOT; }; + 18566B364CC2520CFBDB2607 /* Loop/Resources/Sounds/electronic_lgs_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lgs_medium.caf; sourceTree = SOURCE_ROOT; }; + A746D58F0F0815133AC12A9D /* Loop/Resources/Sounds/electronic_lgs_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_lgs_mild.caf; sourceTree = SOURCE_ROOT; }; + EB5FAFC0C4DA8D94009F0EA7 /* Loop/Resources/Sounds/electronic_ulg_harsh.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_ulg_harsh.caf; sourceTree = SOURCE_ROOT; }; + 6DEA9E9B5623F8F77822D99C /* Loop/Resources/Sounds/electronic_ulg_medium.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_ulg_medium.caf; sourceTree = SOURCE_ROOT; }; + 780F7D310A3BFB3C3F22694C /* Loop/Resources/Sounds/electronic_ulg_mild.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/electronic_ulg_mild.caf; sourceTree = SOURCE_ROOT; }; + 92A703ABE59E0E23337CE6B1 /* Loop/Resources/Sounds/glass_bell.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/glass_bell.caf; sourceTree = SOURCE_ROOT; }; + 8032B80CBB012479E89EBDE0 /* Loop/Resources/Sounds/hard_saw_wave.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/hard_saw_wave.caf; sourceTree = SOURCE_ROOT; }; + 8767531B981D2B1C3284D95E /* Loop/Resources/Sounds/harp.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/harp.caf; sourceTree = SOURCE_ROOT; }; + DE9E9CEE9FBE4CDD17605266 /* Loop/Resources/Sounds/meow.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/meow.caf; sourceTree = SOURCE_ROOT; }; + D212086C7473C12CFAE3EDA7 /* Loop/Resources/Sounds/open_can.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/open_can.caf; sourceTree = SOURCE_ROOT; }; + 47B3FD13DA5BEB4A2CBBA86C /* Loop/Resources/Sounds/open_wrapper.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/open_wrapper.caf; sourceTree = SOURCE_ROOT; }; + 8A58B1B290C65AE2DFC96CE6 /* Loop/Resources/Sounds/saw_wave.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/saw_wave.caf; sourceTree = SOURCE_ROOT; }; + 345BE45BC6D5B01FBA7FE0B4 /* Loop/Resources/Sounds/sine_wave.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/sine_wave.caf; sourceTree = SOURCE_ROOT; }; + 9028674AC1F9A25E599AB2CD /* Loop/Resources/Sounds/voice_hg_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_hg_amy.caf; sourceTree = SOURCE_ROOT; }; + D1C8C12CAA6CF822846035AA /* Loop/Resources/Sounds/voice_hg_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_hg_bryce.caf; sourceTree = SOURCE_ROOT; }; + D5394844C0EA517BF83DAC67 /* Loop/Resources/Sounds/voice_lg_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lg_amy.caf; sourceTree = SOURCE_ROOT; }; + 15A11683B521C116718CCF03 /* Loop/Resources/Sounds/voice_lg_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lg_bryce.caf; sourceTree = SOURCE_ROOT; }; + 34C6B095931D004E1C6D1E2A /* Loop/Resources/Sounds/voice_lgs_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lgs_amy.caf; sourceTree = SOURCE_ROOT; }; + FB860882201269230F24C96C /* Loop/Resources/Sounds/voice_lgs_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_lgs_bryce.caf; sourceTree = SOURCE_ROOT; }; + B08A706BF6BC6555453D3E56 /* Loop/Resources/Sounds/voice_ulg_amy.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_ulg_amy.caf; sourceTree = SOURCE_ROOT; }; + 1F9BFD4BD97793AAE2407DCB /* Loop/Resources/Sounds/voice_ulg_bryce.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop/Resources/Sounds/voice_ulg_bryce.caf; sourceTree = SOURCE_ROOT; }; + A1B2C3D42E8F000100000001 /* GuardAndRunwaySelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardAndRunwaySelectionView.swift; sourceTree = ""; }; 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoopKitTypeAliases.swift; sourceTree = ""; }; 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AlgorithmBridge.swift; sourceTree = ""; }; 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverrideAlgorithmTests.swift; sourceTree = ""; }; @@ -1680,6 +1715,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + B3EE1E6F6E1FF26E47260BCD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A44984BCF1848A652E02CE8C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E9B07F79253BBA6500BAD8F8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1897,6 +1940,7 @@ 437D9BA11D7B5203007245E8 /* Loop.xcconfig */, A951C5FF23E8AB51003E26DC /* Version.xcconfig */, 84957A16300D5CDF0040E2EF /* Recovered References */, + BBB8D9B1F2665786DA6D9ED8 /* Loop Complications */, ); sourceTree = ""; }; @@ -1911,6 +1955,7 @@ 43D9002A21EB209400AF44BF /* LoopCore.framework */, E9B07F7C253BBA6500BAD8F8 /* Loop Intent Extension.appex */, 14B1735C28AED9EC006CCD7C /* Loop Widget Extension.appex */, + AC0853072224AE8CC4C388E7 /* Loop Complications.appex */, ); name = Products; sourceTree = ""; @@ -1952,6 +1997,7 @@ 43F5C2D61B92A4DC003EB13D /* Info.plist */, A966152823EA5A37005D8B29 /* DefaultAssets.xcassets */, A966152923EA5A37005D8B29 /* DerivedAssets.xcassets */, + C535A805BFAB88306874F9E2 /* WatchApp.entitlements */, ); path = WatchApp; sourceTree = ""; @@ -2278,6 +2324,7 @@ A9E6DFED246A0460005B1A1C /* Models */, B4BC56362518DE8800373647 /* ViewModels */, 84EC162D2C9115CA00D220C5 /* DIYLoopUnitTestPlan.xctestplan */, + 9F8AF2728D70D69B00A4F38E /* ComplicationSnapshotTests.swift */, ); path = LoopTests; sourceTree = ""; @@ -2366,6 +2413,7 @@ D7A3000100000000000A0001 /* WatchCGMHost.swift */, D7A3000100000000000A0003 /* WatchDeviceManagers.swift */, 898ECA62218ABD21001E9D35 /* ComplicationChartManager.swift */, + 6515A16900D12040FF31E279 /* ComplicationPublisher.swift */, ); path = Managers; sourceTree = ""; @@ -2465,24 +2513,6 @@ 84957A16300D5CDF0040E2EF /* Recovered References */ = { isa = PBXGroup; children = ( - C9A47B0475B13A9543390BC8 /* Loop/Resources/Sounds/critical.caf */, - 0FC34B523FE5A50C920506D5 /* Loop/Resources/Sounds/alarm.caf */, - 8FB2394478FF679F436E8EF1 /* Loop/Resources/Sounds/bloom.caf */, - AF16A5EDEB9DF756890E0C09 /* Loop/Resources/Sounds/bloop.caf */, - 270F994CD56D566028B3CB25 /* Loop/Resources/Sounds/bright_alarm.caf */, - 3F7A1D72697082DC3A14587E /* Loop/Resources/Sounds/chime.caf */, - 4A645E0E2DDA534A635C9201 /* Loop/Resources/Sounds/clear_chimes.caf */, - 85203550670F2E9BE92F27D8 /* Loop/Resources/Sounds/crying.caf */, - 62F7CB5D5DC337E705032AC7 /* Loop/Resources/Sounds/dings.caf */, - 491F8F0D89972E9D8903CFA7 /* Loop/Resources/Sounds/high_chimes.caf */, - 6D5FCC7207E50ACC682E9E6E /* Loop/Resources/Sounds/honk.caf */, - 74145BE3C46270883D295A1F /* Loop/Resources/Sounds/minimal.caf */, - BEC0BA802BE613F144AC3A70 /* Loop/Resources/Sounds/mood_synth.caf */, - 5181B60CBF3CB2DA537F0475 /* Loop/Resources/Sounds/simple.caf */, - 9471E156FC10D6D0FF219C77 /* Loop/Resources/Sounds/spring.caf */, - 83081FFB0497A4596F597830 /* Loop/Resources/Sounds/synth.caf */, - 21F13EB666EBFDD03F99CB7E /* Loop/Resources/Sounds/trill.caf */, - C9EA3F48338B49BD9C6BD0F9 /* Loop/Resources/Sounds/urgent_low.caf */, A9B1F564432BCBF70C0C6CBB /* Loop/Resources/Sounds/acoustic_hg_harsh.caf */, 3481DFC7974E27ABE6AAC4C3 /* Loop/Resources/Sounds/acoustic_hg_medium.caf */, 1DCC85EA42C8D22C1EC824D8 /* Loop/Resources/Sounds/acoustic_hg_mild.caf */, @@ -2523,6 +2553,24 @@ FB860882201269230F24C96C /* Loop/Resources/Sounds/voice_lgs_bryce.caf */, B08A706BF6BC6555453D3E56 /* Loop/Resources/Sounds/voice_ulg_amy.caf */, 1F9BFD4BD97793AAE2407DCB /* Loop/Resources/Sounds/voice_ulg_bryce.caf */, + C9A47B0475B13A9543390BC8 /* critical.caf */, + 0FC34B523FE5A50C920506D5 /* alarm.caf */, + 8FB2394478FF679F436E8EF1 /* bloom.caf */, + AF16A5EDEB9DF756890E0C09 /* bloop.caf */, + 270F994CD56D566028B3CB25 /* bright_alarm.caf */, + 3F7A1D72697082DC3A14587E /* chime.caf */, + 4A645E0E2DDA534A635C9201 /* clear_chimes.caf */, + 85203550670F2E9BE92F27D8 /* crying.caf */, + 62F7CB5D5DC337E705032AC7 /* dings.caf */, + 491F8F0D89972E9D8903CFA7 /* high_chimes.caf */, + 6D5FCC7207E50ACC682E9E6E /* honk.caf */, + 74145BE3C46270883D295A1F /* minimal.caf */, + BEC0BA802BE613F144AC3A70 /* mood_synth.caf */, + 5181B60CBF3CB2DA537F0475 /* simple.caf */, + 9471E156FC10D6D0FF219C77 /* spring.caf */, + 83081FFB0497A4596F597830 /* synth.caf */, + 21F13EB666EBFDD03F99CB7E /* trill.caf */, + C9EA3F48338B49BD9C6BD0F9 /* urgent_low.caf */, ); name = "Recovered References"; sourceTree = ""; @@ -2793,6 +2841,7 @@ E9B07F86253BBA6500BAD8F8 /* IntentsUI.framework */, 14B1735D28AED9EC006CCD7C /* WidgetKit.framework */, 14B1735F28AED9EC006CCD7C /* SwiftUI.framework */, + CBB18F4A574C9E4041F18D57 /* watchOS */, ); name = Frameworks; sourceTree = ""; @@ -2847,6 +2896,18 @@ path = ViewModels; sourceTree = ""; }; + BBB8D9B1F2665786DA6D9ED8 /* Loop Complications */ = { + isa = PBXGroup; + children = ( + F9C8C0BEB6A6B9A65E0D56AF /* LoopComplications.swift */, + FCF57BFE5DDB939285839898 /* ComplicationSnapshot.swift */, + 2CA0E8DC01120F1A9593061C /* Info.plist */, + 7575B76A6837658308376B90 /* LoopComplications.entitlements */, + ); + name = "Loop Complications"; + path = "Loop Complications"; + sourceTree = ""; + }; C13072B82A76AF0A009A7C58 /* live_capture */ = { isa = PBXGroup; children = ( @@ -2915,6 +2976,14 @@ path = Models; sourceTree = ""; }; + CBB18F4A574C9E4041F18D57 /* watchOS */ = { + isa = PBXGroup; + children = ( + A198971FAA313AF6CDD1ACDF /* Foundation.framework */, + ); + name = watchOS; + sourceTree = ""; + }; E93E86AC24DDE02C00FF40C8 /* Mock Stores */ = { isa = PBXGroup; children = ( @@ -3040,6 +3109,23 @@ productReference = 14B1735C28AED9EC006CCD7C /* Loop Widget Extension.appex */; productType = "com.apple.product-type.app-extension"; }; + 27B118E7F17401410F70FACA /* Loop Complications */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1004EFCBCC83CD0113CAAA67 /* Build configuration list for PBXNativeTarget "Loop Complications" */; + buildPhases = ( + 75E901D28BEE7265730D11E5 /* Sources */, + B3EE1E6F6E1FF26E47260BCD /* Frameworks */, + F70BC2A811EB49CFAF2CA7EE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Loop Complications"; + productName = "Loop Complications"; + productReference = AC0853072224AE8CC4C388E7 /* Loop Complications.appex */; + productType = "com.apple.product-type.app-extension"; + }; 43776F8B1B8022E90074EA36 /* Loop */ = { isa = PBXNativeTarget; buildConfigurationList = 43776FB61B8022E90074EA36 /* Build configuration list for PBXNativeTarget "Loop" */; @@ -3084,11 +3170,13 @@ C1E9CB59294E67060022387B /* Build Derived Assets */, 43A9437A1B926B7B0051FA24 /* Sources */, 43C667D71C5577280050C674 /* Embed Frameworks */, + ECA50E3AF74E45D0C77CA79C /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( C16E94F92E7DBBA600AA4E6E /* PBXTargetDependency */, + 2F20F936EF7D4968D5351400 /* PBXTargetDependency */, ); name = WatchApp; productName = WatchApp; @@ -3316,6 +3404,7 @@ 43D9001A21EB209400AF44BF /* LoopCore-watchOS */, 4F75288A1DFE1DC600C322D6 /* LoopUI */, 43E2D90A1D20C581004DA55F /* LoopTests */, + 27B118E7F17401410F70FACA /* Loop Complications */, ); }; /* End PBXProject section */ @@ -3341,24 +3430,6 @@ C13255D6223E7BE2008AF50C /* BolusProgressTableViewCell.xib in Resources */, C1EE9E812A38D0FB0064784A /* BuildDetails.plist in Resources */, 43FCBBC21E51710B00343C1B /* LaunchScreen.storyboard in Resources */, - C9A47B0480AAB1FC572D3F01 /* Loop/Resources/Sounds/critical.caf in Resources */, - DE965737FE2DAE9FE692769E /* Loop/Resources/Sounds/alarm.caf in Resources */, - A413B305CB4C4009DA09BE57 /* Loop/Resources/Sounds/bloom.caf in Resources */, - 230BE095719B8D8969B32A9C /* Loop/Resources/Sounds/bloop.caf in Resources */, - 9B8FCBDD6E3A9E603C4AABAD /* Loop/Resources/Sounds/bright_alarm.caf in Resources */, - 9960C6964DD51D81093FAD5D /* Loop/Resources/Sounds/chime.caf in Resources */, - 824A01CBE97A4F7A9D32F7D6 /* Loop/Resources/Sounds/clear_chimes.caf in Resources */, - 01414522FC033F2B7A9B223A /* Loop/Resources/Sounds/crying.caf in Resources */, - 2C694BFD5806E573AFAB5E2E /* Loop/Resources/Sounds/dings.caf in Resources */, - A4574F416AD62A1FCDE36D7D /* Loop/Resources/Sounds/high_chimes.caf in Resources */, - FA37F6EC10FF02D4F985D724 /* Loop/Resources/Sounds/honk.caf in Resources */, - 0A02E77AD7536C322933D49B /* Loop/Resources/Sounds/minimal.caf in Resources */, - 33644743F20B3706E3107FAC /* Loop/Resources/Sounds/mood_synth.caf in Resources */, - A2779286E117BD3627DA0618 /* Loop/Resources/Sounds/simple.caf in Resources */, - D6E28676C2C7751C77C7F2FB /* Loop/Resources/Sounds/spring.caf in Resources */, - CB1AF8155B1761A30ACA5591 /* Loop/Resources/Sounds/synth.caf in Resources */, - 16E5A3BC4D931CE6AED18518 /* Loop/Resources/Sounds/trill.caf in Resources */, - 124B300527D84BE2A1233A91 /* Loop/Resources/Sounds/urgent_low.caf in Resources */, 7877942ED674BA37847514AD /* Loop/Resources/Sounds/acoustic_hg_harsh.caf in Resources */, A82DB18B916DC17D2E180D59 /* Loop/Resources/Sounds/acoustic_hg_medium.caf in Resources */, 4A208427654F9A249EC2CFDB /* Loop/Resources/Sounds/acoustic_hg_mild.caf in Resources */, @@ -3399,6 +3470,24 @@ 82F11F858069EDA2DF65D909 /* Loop/Resources/Sounds/voice_lgs_bryce.caf in Resources */, 0996021E82CDE7DA7A3DD544 /* Loop/Resources/Sounds/voice_ulg_amy.caf in Resources */, 8BAF071E821EAC3FB1B183B3 /* Loop/Resources/Sounds/voice_ulg_bryce.caf in Resources */, + C9A47B0480AAB1FC572D3F01 /* critical.caf in Resources */, + DE965737FE2DAE9FE692769E /* alarm.caf in Resources */, + A413B305CB4C4009DA09BE57 /* bloom.caf in Resources */, + 230BE095719B8D8969B32A9C /* bloop.caf in Resources */, + 9B8FCBDD6E3A9E603C4AABAD /* bright_alarm.caf in Resources */, + 9960C6964DD51D81093FAD5D /* chime.caf in Resources */, + 824A01CBE97A4F7A9D32F7D6 /* clear_chimes.caf in Resources */, + 01414522FC033F2B7A9B223A /* crying.caf in Resources */, + 2C694BFD5806E573AFAB5E2E /* dings.caf in Resources */, + A4574F416AD62A1FCDE36D7D /* high_chimes.caf in Resources */, + FA37F6EC10FF02D4F985D724 /* honk.caf in Resources */, + 0A02E77AD7536C322933D49B /* minimal.caf in Resources */, + 33644743F20B3706E3107FAC /* mood_synth.caf in Resources */, + A2779286E117BD3627DA0618 /* simple.caf in Resources */, + D6E28676C2C7751C77C7F2FB /* spring.caf in Resources */, + CB1AF8155B1761A30ACA5591 /* synth.caf in Resources */, + 16E5A3BC4D931CE6AED18518 /* trill.caf in Resources */, + 124B300527D84BE2A1233A91 /* urgent_low.caf in Resources */, B405E35A24D2B1A400DD058D /* HUDAssets.xcassets in Resources */, 1F0000012FBA000100000001 /* AppIcon.icon in Resources */, A966152623EA5A26005D8B29 /* DefaultAssets.xcassets in Resources */, @@ -3484,6 +3573,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F70BC2A811EB49CFAF2CA7EE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -4019,6 +4115,8 @@ 894F6DD3243BCBDB00CCE676 /* Environment+SizeClass.swift in Sources */, C17D52042E7F0578001D2AD2 /* LabelValueRow.swift in Sources */, 895788B2242E69A2002CB114 /* CircularAccessoryButtonStyle.swift in Sources */, + 40ACF455B8FB3B38F2D8C0CA /* ComplicationSnapshot.swift in Sources */, + EB125CD137FD31CD1EFAD0E0 /* ComplicationPublisher.swift in Sources */, 8DC1CE32F39301122B550325 /* LoopKitTypeAliases.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4156,6 +4254,8 @@ E93E86B224DDE21D00FF40C8 /* MockCarbStore.swift in Sources */, C1DA43592B1A784900CBD33F /* MockDeliveryDelegate.swift in Sources */, C18859A02AF1612B0010F21F /* PersistenceController.swift in Sources */, + 6C089A6A6A8AB8F5D835C9F1 /* ComplicationSnapshot.swift in Sources */, + 2615C162995594E8CF144CB2 /* ComplicationSnapshotTests.swift in Sources */, BD37CA2F10445DB600A35199 /* TemporaryScheduleOverrideAlgorithmTests.swift in Sources */, AF79DFAB0F55143D66022668 /* LoopKitTypeAliases.swift in Sources */, ); @@ -4206,6 +4306,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 75E901D28BEE7265730D11E5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3E1A1B91ED0E8C8F5663988E /* LoopComplications.swift in Sources */, + 0956C3313D5165B71E491FFA /* ComplicationSnapshot.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E9B07F78253BBA6500BAD8F8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4234,6 +4343,12 @@ target = 14B1735B28AED9EC006CCD7C /* Loop Widget Extension */; targetProxy = 14B1736728AED9EE006CCD7C /* PBXContainerItemProxy */; }; + 2F20F936EF7D4968D5351400 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Loop Complications"; + target = 27B118E7F17401410F70FACA /* Loop Complications */; + targetProxy = 1B21307ABB67B1D1DACE2D25 /* PBXContainerItemProxy */; + }; 43A943931B926B7B0051FA24 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 43A943711B926B7B0051FA24 /* WatchApp */; @@ -4367,6 +4482,31 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 06B1C4F09B9FA81ADC520CF2 /* Testflight */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = "Loop Complications/LoopComplications.entitlements"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Loop Complications/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(MAIN_APP_BUNDLE_IDENTIFIER).LoopWatch.Complications"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 10.6; + }; + name = Testflight; + }; 14B1736A28AED9EE006CCD7C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4462,6 +4602,30 @@ }; name = Release; }; + 14F587DF4DB6E0D5B2263670 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = "Loop Complications/LoopComplications.entitlements"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Loop Complications/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(MAIN_APP_BUNDLE_IDENTIFIER).LoopWatch.Complications"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 10.6; + }; + name = Debug; + }; 43776FB41B8022E90074EA36 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 437D9BA11D7B5203007245E8 /* Loop.xcconfig */; @@ -4751,6 +4915,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "$(APPICON_NAME)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = WatchApp/WatchApp.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; @@ -4775,6 +4940,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "$(APPICON_NAME)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = WatchApp/WatchApp.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; @@ -4996,6 +5162,31 @@ }; name = Release; }; + 7E2439E4800477E4F2DB20F7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = "Loop Complications/LoopComplications.entitlements"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Loop Complications/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(MAIN_APP_BUNDLE_IDENTIFIER).LoopWatch.Complications"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 10.6; + }; + name = Release; + }; B4E7CF912AD00A39009B4DF2 /* Testflight */ = { isa = XCBuildConfiguration; baseConfigurationReference = 437D9BA11D7B5203007245E8 /* Loop.xcconfig */; @@ -5142,6 +5333,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "$(APPICON_NAME)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = WatchApp/WatchApp.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; @@ -5395,6 +5587,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 1004EFCBCC83CD0113CAAA67 /* Build configuration list for PBXNativeTarget "Loop Complications" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7E2439E4800477E4F2DB20F7 /* Release */, + 14F587DF4DB6E0D5B2263670 /* Debug */, + 06B1C4F09B9FA81ADC520CF2 /* Testflight */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 14B1736C28AED9EE006CCD7C /* Build configuration list for PBXNativeTarget "Loop Widget Extension" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/LoopTests/ComplicationSnapshotTests.swift b/LoopTests/ComplicationSnapshotTests.swift new file mode 100644 index 0000000000..6eea74bcf6 --- /dev/null +++ b/LoopTests/ComplicationSnapshotTests.swift @@ -0,0 +1,85 @@ +// +// ComplicationSnapshotTests.swift +// LoopTests +// +// The watch complications' snapshot (compiled into this target from Loop Complications): what shows at +// each moment, the timeline's change moments, and when the watch app asks WidgetKit to reload. +// + +import XCTest + +final class ComplicationSnapshotTests: XCTestCase { + + private let now = Date() + + private func snapshot(glucoseAge: TimeInterval = 60, loopAge: TimeInterval = 60) -> ComplicationSnapshot { + let reading = now.addingTimeInterval(-glucoseAge) + let loop = now.addingTimeInterval(-loopAge) + return ComplicationSnapshot(glucoseText: "106", trendSymbol: "↗", staleGlucoseText: "---", + glucoseStaleAt: reading.addingTimeInterval(15 * 60), eventualText: "108", + inlineText: "106↗ 108 8:47 PM", inlineStaleText: "--- 108 8:47 PM", + loopDate: loop, freshUntil: loop.addingTimeInterval(6 * 60), + agingUntil: loop.addingTimeInterval(16 * 60), graph: nil) + } + + func testAFreshReadingShowsWithItsTrend() { + let s = snapshot() + XCTAssertEqual(s.glucose(at: now), "106") + XCTAssertEqual(s.trend(at: now), "↗") + XCTAssertEqual(s.inline(at: now), "106↗ 108 8:47 PM") + } + + /// As the ClockKit templates: a stale reading shows the stale marker and drops the trend. + func testAStaleReadingShowsTheMarkerAndNoTrend() { + let s = snapshot(glucoseAge: 16 * 60) + XCTAssertEqual(s.glucose(at: now), "---") + XCTAssertEqual(s.trend(at: now), "") + XCTAssertEqual(s.inline(at: now), "--- 108 8:47 PM") + } + + func testLoopFreshnessFollowsTheLoopsAge() { + XCTAssertEqual(snapshot(loopAge: 60).freshness(at: now), .fresh) + XCTAssertEqual(snapshot(loopAge: 10 * 60).freshness(at: now), .aging) + XCTAssertEqual(snapshot(loopAge: 20 * 60).freshness(at: now), .stale) + var noLoop = snapshot() + noLoop.loopDate = nil + XCTAssertEqual(noLoop.freshness(at: now), .stale) + } + + /// The timeline carries the moments things change on their own, so none of them needs a reload. + func testTheTimelineMarksEachChangeMoment() { + let s = snapshot(glucoseAge: 60, loopAge: 60) + let moments = s.changeMoments(after: now) + XCTAssertEqual(moments.count, 3, "reading stale, loop aging, loop stale") + XCTAssertEqual(s.freshness(at: moments[0]), .aging, "first: the loop turns at 6 min") + XCTAssertTrue(s.glucoseIsStale(at: moments[1]), "then the reading at 15 min") + XCTAssertEqual(s.freshness(at: moments[2]), .stale, "then the loop at 16 min") + } + + func testTheSnapshotRoundTripsThroughDefaults() { + let defaults = UserDefaults(suiteName: "ComplicationSnapshotTests")! + defaults.removePersistentDomain(forName: "ComplicationSnapshotTests") + snapshot().save(to: defaults) + XCTAssertEqual(ComplicationSnapshot.load(from: defaults), snapshot()) + + let withGraph = ComplicationSnapshot.sample + withGraph.save(to: defaults) + XCTAssertEqual(ComplicationSnapshot.load(from: defaults)?.graph, withGraph.graph) + } + + /// Reloads are budgeted: at most one per window, and a change inside the window is not lost. + func testReloadsAreThrottledButNeverLost() { + var throttle = ComplicationReloadThrottle() + let a = snapshot(), b = snapshot(glucoseAge: 0) + + XCTAssertEqual(throttle.offer(a, now: now).reload, true, "the first value reloads at once") + let unchanged = throttle.offer(a, now: now.addingTimeInterval(60)) + XCTAssertFalse(unchanged.save, "an unchanged value is not rewritten") + XCTAssertFalse(unchanged.reload) + let early = throttle.offer(b, now: now.addingTimeInterval(120)) + XCTAssertTrue(early.save) + XCTAssertFalse(early.reload, "inside the window: saved, reload owed") + XCTAssertTrue(throttle.offer(b, now: now.addingTimeInterval(301)).reload, "the owed reload is paid once the window passes") + XCTAssertFalse(throttle.offer(b, now: now.addingTimeInterval(700)).reload, "nothing owed, nothing reloaded") + } +} diff --git a/WatchApp Extension/ComplicationController.swift b/WatchApp Extension/ComplicationController.swift index 7ce17628a5..475f6dc8cf 100644 --- a/WatchApp Extension/ComplicationController.swift +++ b/WatchApp Extension/ComplicationController.swift @@ -239,3 +239,21 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { } } } + +// MARK: - Migration to WidgetKit + +/// Faces that show a ClockKit complication from this app are moved to its WidgetKit replacement when +/// the app updates. Both ClockKit descriptors share the identifier "glucosegraph", so they are told +/// apart by family: the graph is the one offered in the large rectangular slot. +extension ComplicationController: CLKComplicationWidgetMigrator { + + var widgetMigrator: CLKComplicationWidgetMigrator { self } + + func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? { + guard let appBundle = Bundle.main.bundleIdentifier else { return nil } + let kind = complicationDescriptor.supportedFamilies.contains(.graphicRectangular) + ? LoopComplicationKind.glucoseGraph + : LoopComplicationKind.loopStatus + return CLKComplicationStaticWidgetMigrationConfiguration(kind: kind, extensionBundleIdentifier: appBundle + ".Complications") + } +} diff --git a/WatchApp Extension/ExtensionDelegate.swift b/WatchApp Extension/ExtensionDelegate.swift index 5d7a1b361f..d098db0382 100644 --- a/WatchApp Extension/ExtensionDelegate.swift +++ b/WatchApp Extension/ExtensionDelegate.swift @@ -279,6 +279,7 @@ class ExtensionDelegate: NSObject, WKApplicationDelegate { log.default("Reloading complication timeline") server.reloadTimeline(for: complication) } + ComplicationPublisher.publish(from: loopManager) } } diff --git a/WatchApp Extension/Managers/ComplicationPublisher.swift b/WatchApp Extension/Managers/ComplicationPublisher.swift new file mode 100644 index 0000000000..0b96e8684d --- /dev/null +++ b/WatchApp Extension/Managers/ComplicationPublisher.swift @@ -0,0 +1,107 @@ +// +// ComplicationPublisher.swift +// WatchApp Extension +// +// Feeds the Loop Complications widget extension. On every context update the watch app formats what +// the complications show (with the same formatters and localized strings the ClockKit templates +// used), gathers the glucose graph's data for the widget to draw, writes both to the shared app +// group, and asks WidgetKit to reload (ComplicationReloadThrottle decides when). +// + +import Foundation +import LoopCore +import LoopKit +import WatchKit +import WidgetKit + +enum ComplicationPublisher { + + private static var throttle = ComplicationReloadThrottle() + + /// Called on main at each context update. + @MainActor static func publish(from loopManager: LoopDataManager) { + guard let context = loopManager.activeContext else { return } + Task { @MainActor in + let now = Date() + let graph = await loopManager.generateChartData().map { self.graph(from: $0, now: now) } + store(snapshot(from: context, graph: graph), now: now) + } + } + + /// The graph's data as stock's ClockKit chart drew it, kept an extra hour ahead for later timeline entries. + static func graph(from data: GlucoseChartData, now: Date) -> ComplicationGraph { + let unit = data.unit ?? .milligramsPerDeciliter + let span = ComplicationGraph.halfSpan + let visible = DateInterval(start: now - span, end: now + span) + let kept = DateInterval(start: now - span, end: now + span + .hours(1)) + let range = data.chartableGlucoseRange(from: visible) + let formatter = NumberFormatter.glucoseFormatter(for: unit) + + let targetRanges = data.correctionRange?.quantityBetween(start: kept.start, end: kept.end).map { + ComplicationGraph.Band(start: $0.startDate, end: $0.endDate, + min: $0.value.lowerBound.doubleValue(for: unit), max: $0.value.upperBound.doubleValue(for: unit)) + } ?? [] + let override = data.activeScheduleOverride.flatMap(TemporaryScheduleOverrideHashable.init).map { + ComplicationGraph.Band(start: $0.start, end: $0.end, + min: $0.min.doubleValue(for: unit), max: $0.max.doubleValue(for: unit)) + } + let historical = (data.historicalGlucose ?? []).filter { kept.contains($0.startDate) }.map { + ComplicationGraph.Point(date: $0.startDate, value: $0.quantity.doubleValue(for: unit), isManual: $0.isManualGlucose) + } + let predicted = (data.predictedGlucose ?? []).filter { kept.contains($0.startDate) }.map { + ComplicationGraph.Point(date: $0.startDate, value: $0.quantity.doubleValue(for: unit)) + } + let glucoseMin = range.lowerBound.doubleValue(for: unit) + let glucoseMax = range.upperBound.doubleValue(for: unit) + return ComplicationGraph(glucoseMin: glucoseMin, glucoseMax: glucoseMax, + minLabel: formatter.string(from: glucoseMin) ?? "", maxLabel: formatter.string(from: glucoseMax) ?? "", + targetRanges: targetRanges, override: override, historical: historical, predicted: predicted) + } + + /// The strings each complication shows, formatted as the ClockKit templates formatted them. + static func snapshot(from context: WatchContext, graph: ComplicationGraph?) -> ComplicationSnapshot { + let unit = context.displayGlucoseUnit ?? .milligramsPerDeciliter + let formatter = NumberFormatter.glucoseFormatter(for: unit) + let staleGlucose = NSLocalizedString("---", comment: "No glucose value representation (3 dashes for mg/dL; no spaces as this will get truncated in the watch complication)") + + let glucoseText: String? = context.glucoseCondition?.localizedDescription + ?? context.glucose.flatMap { formatter.string(from: $0.doubleValue(for: unit)) } + let trendSymbol = context.glucoseTrend?.symbol + let eventualText = context.eventualGlucose.flatMap { formatter.string(from: $0.doubleValue(for: unit)) } + + var inlineText: String? + var inlineStaleText: String? + if let glucoseDate = context.glucoseDate { + let timeFormatter = DateFormatter() + timeFormatter.dateStyle = .none + timeFormatter.timeStyle = .short + // 106↗108 8:47 PM + let format = NSLocalizedString("UtilitarianLargeFlat", tableName: "ckcomplication", comment: "Utilitarian large flat format string (1: Glucose & Trend symbol) (2: Eventual Glucose) (3: Time)") + let time = timeFormatter.string(from: glucoseDate) + inlineText = String(format: format, "\(glucoseText ?? staleGlucose)\(trendSymbol ?? " ")", eventualText ?? "", time) + inlineStaleText = String(format: format, staleGlucose, eventualText ?? "", time) + } + + let loopDate = context.loopLastRunDate + return ComplicationSnapshot( + glucoseText: glucoseText, + trendSymbol: trendSymbol, + staleGlucoseText: staleGlucose, + glucoseStaleAt: context.glucoseDate.map { $0.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval) }, + eventualText: eventualText, + inlineText: inlineText, + inlineStaleText: inlineStaleText, + loopDate: loopDate, + freshUntil: loopDate.flatMap { date in LoopCompletionFreshness.fresh.maxAge.map { date.addingTimeInterval($0) } }, + agingUntil: loopDate.flatMap { date in LoopCompletionFreshness.aging.maxAge.map { date.addingTimeInterval($0) } }, + graph: graph + ) + } + + @MainActor private static func store(_ snapshot: ComplicationSnapshot, now: Date) { + let decision = throttle.offer(snapshot, now: now) + if decision.save { snapshot.save() } + // Only these kinds: WidgetKit budgets reloads per widget, and other widgets keep their own. + if decision.reload { LoopComplicationKind.all.forEach { WidgetCenter.shared.reloadTimelines(ofKind: $0) } } + } +} diff --git a/WatchApp/Info.plist b/WatchApp/Info.plist index 08ccae82d9..8749b71433 100644 --- a/WatchApp/Info.plist +++ b/WatchApp/Info.plist @@ -2,12 +2,16 @@ + AppGroupIdentifier + $(APP_GROUP_IDENTIFIER) CFBundleDevelopmentRegion en CFBundleDisplayName $(MAIN_APP_DISPLAY_NAME) CFBundleExecutable $(EXECUTABLE_NAME) + CFBundleIconName + AppIcon CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -47,8 +51,6 @@ WKCompanionAppBundleIdentifier $(MAIN_APP_BUNDLE_IDENTIFIER) - CFBundleIconName - AppIcon WKSupportsLiveActivityLaunchAttributeTypes GlucoseActivityAttributes diff --git a/WatchApp/WatchApp.entitlements b/WatchApp/WatchApp.entitlements new file mode 100644 index 0000000000..d9849a816d --- /dev/null +++ b/WatchApp/WatchApp.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + $(APP_GROUP_IDENTIFIER) + + +