diff --git a/.gitignore b/.gitignore
index 5a750a7dba..25319f7984 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,7 +22,6 @@ xcuserdata
*.moved-aside
*.xcuserstate
*.xcscmblueprint
-project.xcworkspace
## Obj-C/Swift specific
*.hmap
@@ -55,7 +54,7 @@ Carthage/
# fastlane
#
-# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
@@ -71,3 +70,7 @@ RemoteSettings.plist
# Framework development
Loop.xcworkspace
+
+# Avoid checking in built assets
+Loop/DerivedAssets.xcassets/
+WatchApp/DerivedAssets.xcassets/
diff --git a/.travis.yml b/.travis.yml
index d695f5dfe4..c582b6b5a0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,21 @@
language: objective-c
-osx_image: xcode10
+osx_image: xcode11
# xcode_sdk: iphonesimulator11
# xcode_project: Loop.xcodeproj
# xcode_scheme: Loop
+
+addons:
+ homebrew:
+ packages:
+ - carthage
+ update: true
before_script:
+ - git config --global protocol.version 1
- set -o pipefail && xcodebuild -project Loop.xcodeproj -target Cartfile
script:
# Build the app target
- - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme Loop build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty
+ - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme Loop build CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO | xcpretty
+ - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme Learn build CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO | xcpretty
# Run the test target
- - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme LoopTests -destination 'name=iPhone SE' test | xcpretty
- - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme DoseMathTests -destination 'name=iPhone SE' test | xcpretty
+ - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme LoopTests -destination 'name=iPhone 8' test | xcpretty
+ - set -o pipefail && xcodebuild -project Loop.xcodeproj -scheme DoseMathTests -destination 'name=iPhone 8' test | xcpretty
diff --git a/Cartfile b/Cartfile
index fa7ce1de3a..cea05676a6 100644
--- a/Cartfile
+++ b/Cartfile
@@ -1,8 +1,8 @@
-
-github "LoopKit/LoopKit" ~> 2.2
-github "LoopKit/CGMBLEKit" == 3.0
-github "ps2/SwiftCharts" "hotfix-xcode10.2"
-github "LoopKit/dexcom-share-client-swift" == 1.0
-github "LoopKit/G4ShareSpy" == 1.0
-github "ps2/rileylink_ios" ~> 2.1
+github "LoopKit/LoopKit" ~> 3.0
+github "LoopKit/CGMBLEKit" ~> 3.2
+github "i-schuetz/SwiftCharts" == 0.6.5
+github "LoopKit/dexcom-share-client-swift" ~> 1.2
+github "LoopKit/G4ShareSpy" ~> 1.1
+github "ps2/rileylink_ios" ~> 3.0
github "LoopKit/Amplitude-iOS" "decreepify"
+github "cyoung1024/spike-client-swift-195" "master"
diff --git a/Cartfile.resolved b/Cartfile.resolved
index a72a5e3409..5c335ac041 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1,7 +1,9 @@
github "LoopKit/Amplitude-iOS" "2137d5fd44bf630ed33e1e72d7af6d8f8612f270"
-github "LoopKit/CGMBLEKit" "v3.0"
-github "LoopKit/G4ShareSpy" "v1.0"
-github "LoopKit/LoopKit" "v2.2.2"
-github "LoopKit/dexcom-share-client-swift" "v1.0"
-github "ps2/SwiftCharts" "cc8c401171d5ccb638ec6c87f6c410ee31fa774d"
-github "ps2/rileylink_ios" "v2.1.1"
+github "LoopKit/CGMBLEKit" "v3.2"
+github "LoopKit/G4ShareSpy" "v1.1"
+github "LoopKit/LoopKit" "v3.0"
+github "LoopKit/MKRingProgressView" "f548a5c64832be2d37d7c91b5800e284887a2a0a"
+github "LoopKit/dexcom-share-client-swift" "v1.2"
+github "cyoung1024/spike-client-swift-195" "20035cffcf752c2c9b5b35d88ee0fe63c16155e6"
+github "i-schuetz/SwiftCharts" "0.6.5"
+github "ps2/rileylink_ios" "v3.0"
diff --git a/Common/Base.lproj/Intents.intentdefinition b/Common/Base.lproj/Intents.intentdefinition
index 5a963140de..7dccff8c62 100644
--- a/Common/Base.lproj/Intents.intentdefinition
+++ b/Common/Base.lproj/Intents.intentdefinition
@@ -7,16 +7,18 @@
INIntentDefinitionModelVersion
1.0
INIntentDefinitionSystemVersion
- 17G65
+ 18G87
INIntentDefinitionToolsBuildVersion
- 10L232m
+ 10G8
INIntentDefinitionToolsVersion
- 10.0
+ 10.3
INIntents
INIntentCategory
create
+ INIntentDescription
+ Add a carb entry to Loop
INIntentDescriptionID
yc02Yq
INIntentLastParameterTag
diff --git a/Common/Extensions/GlucoseRangeSchedule.swift b/Common/Extensions/GlucoseRangeSchedule.swift
index 4cd1712a52..c8ef74fedb 100644
--- a/Common/Extensions/GlucoseRangeSchedule.swift
+++ b/Common/Extensions/GlucoseRangeSchedule.swift
@@ -10,58 +10,16 @@ import HealthKit
extension GlucoseRangeSchedule {
- func overrideEnabledForContext(_ context: Override.Context) -> Bool? {
- guard let override = override, override.context == context else {
- guard let value = overrideRanges[context], !value.isZero else {
- // Unavailable to set
- return nil
- }
-
- return false
- }
-
- return override.isActive()
- }
-
- var activeOverride: GlucoseRangeSchedule.Override? {
- guard let override = override, override.isActive() else {
- return nil
- }
-
- return override
- }
-
- var activeOverrideContext: GlucoseRangeSchedule.Override.Context? {
- return activeOverride?.context
- }
-
- var activeOverrideQuantityRange: Range? {
- guard let activeOverride = activeOverride else {
- return nil
- }
-
- let lowerBound = HKQuantity(unit: unit, doubleValue: activeOverride.value.minValue)
- let upperBound = HKQuantity(unit: unit, doubleValue: activeOverride.value.maxValue)
- return lowerBound.. HKQuantity {
return HKQuantity(unit: unit, doubleValue: value(at: date).minValue)
}
}
-extension DoubleRange {
- var averageValue: Double {
+extension ClosedRange where Bound == HKQuantity {
+ func averageValue(for unit: HKUnit) -> Double {
+ let minValue = lowerBound.doubleValue(for: unit)
+ let maxValue = upperBound.doubleValue(for: unit)
return (maxValue + minValue) / 2
}
}
diff --git a/Common/Extensions/HKUnit.swift b/Common/Extensions/HKUnit.swift
index 961414a265..36f7576d80 100644
--- a/Common/Extensions/HKUnit.swift
+++ b/Common/Extensions/HKUnit.swift
@@ -7,6 +7,7 @@
//
import HealthKit
+import LoopCore
// Code in this extension is duplicated from:
// https://github.com/LoopKit/LoopKit/blob/master/LoopKit/HKUnit.swift
@@ -21,14 +22,6 @@ extension HKUnit {
}
}
- static let milligramsPerDeciliter: HKUnit = {
- return HKUnit.gramUnit(with: .milli).unitDivided(by: HKUnit.literUnit(with: .deci))
- }()
-
- static let millimolesPerLiter: HKUnit = {
- return HKUnit.moleUnit(with: .milli, molarMass: HKUnitMolarMassBloodGlucose).unitDivided(by: HKUnit.liter())
- }()
-
var localizedShortUnitString: String {
if self == HKUnit.millimolesPerLiter {
return NSLocalizedString("mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
@@ -43,15 +36,6 @@ extension HKUnit {
}
}
- /// An example value for the "ideal" target
- var glucoseExampleTargetValue: Double {
- if self == .milligramsPerDeciliter {
- return 100
- } else {
- return 5.5
- }
- }
-
/// The smallest value expected to be visible on a chart
var chartableIncrement: Double {
if self == .milligramsPerDeciliter {
diff --git a/Common/Extensions/NSBundle.swift b/Common/Extensions/NSBundle.swift
index e9202624d6..b5f2011274 100644
--- a/Common/Extensions/NSBundle.swift
+++ b/Common/Extensions/NSBundle.swift
@@ -30,6 +30,10 @@ extension Bundle {
return object(forInfoDictionaryKey: "AppGroupIdentifier") as! String
}
+ var isAppExtension: Bool {
+ return bundleURL.pathExtension == "appex"
+ }
+
var mainAppUrl: URL? {
if let mainAppBundleIdentifier = mainAppBundleIdentifier {
return URL(string: "\(mainAppBundleIdentifier)://")
@@ -37,4 +41,24 @@ extension Bundle {
return nil
}
}
+
+ var gitRevision: String? {
+ return object(forInfoDictionaryKey: "com-loopkit-Loop-git-revision") as? String
+ }
+
+ var gitBranch: String? {
+ return object(forInfoDictionaryKey: "com-loopkit-Loop-git-branch") as? String
+ }
+
+ var sourceRoot: String? {
+ return object(forInfoDictionaryKey: "com-loopkit-Loop-srcroot") as? String
+ }
+
+ var buildDateString: String? {
+ return object(forInfoDictionaryKey: "com-loopkit-Loop-build-date") as? String
+ }
+
+ var xcodeVersion: String? {
+ return object(forInfoDictionaryKey: "com-loopkit-Loop-xcode-version") as? String
+ }
}
diff --git a/Common/Extensions/NSTimeInterval.swift b/Common/Extensions/NSTimeInterval.swift
index be19ded20d..2375f27cc9 100644
--- a/Common/Extensions/NSTimeInterval.swift
+++ b/Common/Extensions/NSTimeInterval.swift
@@ -37,4 +37,5 @@ extension TimeInterval {
var hours: Double {
return minutes / 60.0
}
+
}
diff --git a/Common/Extensions/NewCarbEntryIntent+Loop.swift b/Common/Extensions/NewCarbEntryIntent+Loop.swift
index 11d21f0297..0f2419ffa9 100644
--- a/Common/Extensions/NewCarbEntryIntent+Loop.swift
+++ b/Common/Extensions/NewCarbEntryIntent+Loop.swift
@@ -6,6 +6,7 @@
//
import Foundation
+import LoopCore
@available(iOS 12.0, watchOSApplicationExtension 5.0, *)
extension NewCarbEntryIntent: IdentifiableClass { }
diff --git a/Common/Extensions/SampleValue.swift b/Common/Extensions/SampleValue.swift
index d554b7cd1d..39dcb16e9c 100644
--- a/Common/Extensions/SampleValue.swift
+++ b/Common/Extensions/SampleValue.swift
@@ -11,7 +11,7 @@ import LoopKit
extension Collection where Element == SampleValue {
/// O(n)
- var quantityRange: Range? {
+ var quantityRange: ClosedRange? {
var lowest: HKQuantity?
var highest: HKQuantity?
@@ -33,6 +33,6 @@ extension Collection where Element == SampleValue {
return nil
}
- return l.. UIColor {
- return UIColor(red: 90 / 255, green: 200 / 255, blue: 250 / 255, alpha: 1)
- }
-
- static func HIGYellowColor() -> UIColor {
- return UIColor(red: 1, green: 204 / 255, blue: 0, alpha: 1)
- }
-
- static func HIGOrangeColor() -> UIColor {
- return UIColor(red: 1, green: 149 / 255, blue: 0 / 255, alpha: 1)
- }
-
- static func HIGPinkColor() -> UIColor {
- return UIColor(red: 1, green: 45 / 255, blue: 85 / 255, alpha: 1)
- }
-
- static func HIGBlueColor() -> UIColor {
- return UIColor(red: 0, green: 122 / 255, blue: 1, alpha: 1)
- }
-
+ // HIG Green has changed for iOS 13. This is the legacy color.
static func HIGGreenColor() -> UIColor {
return UIColor(red: 76 / 255, green: 217 / 255, blue: 100 / 255, alpha: 1)
}
-
- static func HIGRedColor() -> UIColor {
- return UIColor(red: 1, green: 59 / 255, blue: 48 / 255, alpha: 1)
- }
-
- static func HIGPurpleColor() -> UIColor {
- return UIColor(red: 88 / 255, green: 86 / 255, blue: 214 / 255, alpha: 1)
- }
-
- static func HIGGrayColor() -> UIColor {
- return UIColor(red: 142 / 255, green: 143 / 255, blue: 147 / 255, alpha: 1)
- }
-
}
diff --git a/Common/Extensions/UIColor.swift b/Common/Extensions/UIColor.swift
index 452d62161c..ad6bd3cca0 100644
--- a/Common/Extensions/UIColor.swift
+++ b/Common/Extensions/UIColor.swift
@@ -10,21 +10,57 @@ import UIKit
extension UIColor {
- @nonobjc static var tintColor: UIColor? = nil
+ @nonobjc static let secondaryLabelColor: UIColor = {
+ if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
+ return UIColor.secondaryLabel
+ } else {
+ return UIColor.systemGray
+ }
+ }()
- @nonobjc static let secondaryLabelColor = UIColor.HIGGrayColor()
+ @nonobjc static let cellBackgroundColor: UIColor = {
+ if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
+ return .secondarySystemBackground
+ } else {
+ return UIColor(white: 239 / 255, alpha: 1)
+ }
+ }()
- @nonobjc static let cellBackgroundColor = UIColor(white: 239 / 255, alpha: 1)
+ @nonobjc static let IOBTintColor = UIColor.systemOrange
- @nonobjc static let IOBTintColor = UIColor.HIGOrangeColor()
+ @nonobjc static let COBTintColor: UIColor = {
+ if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
+ return UIColor(dynamicProvider: { (traitCollection) in
+ // If we're in accessibility mode, return the system color
+ guard case .normal = traitCollection.accessibilityContrast else {
+ return .systemGreen
+ }
- @nonobjc static let COBTintColor = UIColor(red: 99 / 255, green: 218 / 255, blue: 56 / 255, alpha: 1)
+ switch traitCollection.userInterfaceStyle {
+ case .unspecified, .light:
+ return UIColor(red: 99 / 255, green: 218 / 255, blue: 56 / 255, alpha: 1)
+ case .dark:
+ return UIColor(red: 89 / 255, green: 228 / 255, blue: 51 / 255, alpha: 1)
+ @unknown default:
+ return UIColor(red: 99 / 255, green: 218 / 255, blue: 56 / 255, alpha: 1)
+ }
+ })
+ } else {
+ return UIColor(red: 99 / 255, green: 218 / 255, blue: 56 / 255, alpha: 1)
+ }
+ }()
- @nonobjc static let agingColor = UIColor.HIGYellowColor()
+ @nonobjc static let agingColor = UIColor.systemYellow
- @nonobjc static let staleColor = UIColor.HIGRedColor()
+ @nonobjc static let staleColor = UIColor.systemRed
- @nonobjc static let unknownColor = UIColor(red: 198 / 255, green: 199 / 255, blue: 201 / 255, alpha: 1)
+ @nonobjc static let unknownColor: UIColor = {
+ if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
+ return .systemGray4
+ } else {
+ return UIColor(red: 198 / 255, green: 199 / 255, blue: 201 / 255, alpha: 1)
+ }
+ }()
- static let delete = UIColor.HIGRedColor()
+ static let delete = UIColor.systemRed
}
diff --git a/Common/FeatureFlags.swift b/Common/FeatureFlags.swift
new file mode 100644
index 0000000000..bc27a15439
--- /dev/null
+++ b/Common/FeatureFlags.swift
@@ -0,0 +1,25 @@
+//
+// FeatureFlags.swift
+// Loop
+//
+// Created by Michael Pangburn on 5/19/19.
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+let FeatureFlags = FeatureFlagConfiguration()
+
+struct FeatureFlagConfiguration: Decodable {
+ let sensitivityOverridesEnabled: Bool
+
+ fileprivate init() {
+ // Swift compiler config is inverse, since the default state is enabled.
+ #if FEATURE_OVERRIDES_DISABLED
+ self.sensitivityOverridesEnabled = false
+ #else
+ self.sensitivityOverridesEnabled = true
+ #endif
+ }
+}
diff --git a/Common/Models/BolusSuggestionUserInfo.swift b/Common/Models/BolusSuggestionUserInfo.swift
deleted file mode 100644
index 2a7023b6fe..0000000000
--- a/Common/Models/BolusSuggestionUserInfo.swift
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// BolusSuggestionUserInfo.swift
-// Naterade
-//
-// Created by Nathan Racklyeft on 3/20/16.
-// Copyright © 2016 Nathan Racklyeft. All rights reserved.
-//
-
-import Foundation
-
-
-struct BolusSuggestionUserInfo: RawRepresentable {
- let recommendedBolus: Double?
- var maxBolus: Double?
-
- init(recommendedBolus: Double?, maxBolus: Double? = nil) {
- self.recommendedBolus = recommendedBolus
- self.maxBolus = maxBolus
- }
-
- // MARK: - RawRepresentable
- typealias RawValue = [String: Any]
-
- static let version = 1
- static let name = "BolusSuggestionUserInfo"
-
- init?(rawValue: RawValue) {
- guard rawValue["v"] as? Int == type(of: self).version && rawValue["name"] as? String == BolusSuggestionUserInfo.name,
- let recommendedBolus = rawValue["br"] as? Double else
- {
- return nil
- }
-
- self.recommendedBolus = recommendedBolus
- self.maxBolus = rawValue["mb"] as? Double
- }
-
- var rawValue: RawValue {
- var raw: RawValue = [
- "v": type(of: self).version,
- "name": BolusSuggestionUserInfo.name,
- ]
-
- raw["br"] = recommendedBolus
- raw["mb"] = maxBolus
-
- return raw
- }
-}
diff --git a/Common/Models/CarbEntryUserInfo.swift b/Common/Models/CarbEntryUserInfo.swift
deleted file mode 100644
index 4b1a6285a4..0000000000
--- a/Common/Models/CarbEntryUserInfo.swift
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// CarbEntryUserInfo.swift
-// Naterade
-//
-// Created by Nathan Racklyeft on 1/23/16.
-// Copyright © 2016 Nathan Racklyeft. All rights reserved.
-//
-
-import Foundation
-
-enum AbsorptionTimeType {
- case fast
- case medium
- case slow
-}
-
-
-struct CarbEntryUserInfo {
- let value: Double
- let absorptionTimeType: AbsorptionTimeType
- let startDate: Date
-
- init(value: Double, absorptionTimeType: AbsorptionTimeType, startDate: Date) {
- self.value = value
- self.absorptionTimeType = absorptionTimeType
- self.startDate = startDate
- }
-}
-
-
-extension AbsorptionTimeType: RawRepresentable {
- typealias RawValue = Int
-
- init?(rawValue: RawValue) {
- switch rawValue {
- case 0:
- self = .fast
- case 1:
- self = .medium
- case 2:
- self = .slow
- default:
- return nil
- }
- }
-
- var rawValue: RawValue {
- switch self {
- case .fast:
- return 0
- case .medium:
- return 1
- case .slow:
- return 2
- }
- }
-}
-
-
-extension CarbEntryUserInfo: RawRepresentable {
- typealias RawValue = [String: Any]
-
- static let version = 1
- static let name = "CarbEntryUserInfo"
-
- init?(rawValue: RawValue) {
- guard rawValue["v"] as? Int == type(of: self).version && rawValue["name"] as? String == CarbEntryUserInfo.name,
- let value = rawValue["cv"] as? Double,
- let absorptionTimeRaw = rawValue["ca"] as? Int,
- let absorptionTime = AbsorptionTimeType(rawValue: absorptionTimeRaw),
- let startDate = rawValue["sd"] as? Date else
- {
- return nil
- }
-
- self.value = value
- self.startDate = startDate
- self.absorptionTimeType = absorptionTime
- }
-
- var rawValue: RawValue {
- return [
- "v": type(of: self).version,
- "name": CarbEntryUserInfo.name,
- "cv": value,
- "ca": absorptionTimeType.rawValue,
- "sd": startDate
- ]
- }
-}
diff --git a/Common/Models/Insulin/ExponentialInsulinModelPreset.swift b/Common/Models/Insulin/ExponentialInsulinModelPreset.swift
deleted file mode 100644
index 66644da1b8..0000000000
--- a/Common/Models/Insulin/ExponentialInsulinModelPreset.swift
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// ExponentialInsulinModelPreset.swift
-// Loop
-//
-// Copyright © 2017 LoopKit Authors. All rights reserved.
-//
-
-import LoopKit
-
-
-enum ExponentialInsulinModelPreset: String {
- case humalogNovologAdult
- case humalogNovologChild
- case fiasp
-}
-
-
-// MARK: - Model generation
-extension ExponentialInsulinModelPreset {
- var actionDuration: TimeInterval {
- switch self {
- case .humalogNovologAdult:
- return .minutes(360)
- case .humalogNovologChild:
- return .minutes(360)
- case .fiasp:
- return .minutes(360)
- }
- }
-
- var peakActivity: TimeInterval {
- switch self {
- case .humalogNovologAdult:
- return .minutes(75)
- case .humalogNovologChild:
- return .minutes(65)
- case .fiasp:
- return .minutes(55)
- }
- }
-
- var model: InsulinModel {
- return ExponentialInsulinModel(actionDuration: actionDuration, peakActivityTime: peakActivity)
- }
-}
-
-
-// MARK: - Localization
-extension ExponentialInsulinModelPreset {
- var title: String {
- switch self {
- case .humalogNovologAdult:
- return NSLocalizedString("Rapid-Acting – Adults", comment: "Title of insulin model preset")
- case .humalogNovologChild:
- return NSLocalizedString("Rapid-Acting – Children", comment: "Title of insulin model preset")
- case .fiasp:
- return NSLocalizedString("Fiasp", comment: "Title of insulin model preset")
- }
- }
-
- var subtitle: String? {
- switch self {
- case .humalogNovologAdult:
- return NSLocalizedString("A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults.", comment: "Subtitle of Rapid-Acting – Adult preset")
- case .humalogNovologChild:
- return NSLocalizedString("An adjustment to the adult model based on empirical effects in children.", comment: "Subtitle of Rapid-Acting – Children preset")
- case .fiasp:
- return NSLocalizedString("A model based on the published absorption of Fiasp insulin.", comment: "Subtitle of Fiasp preset")
- }
- }
-}
-
-
-extension ExponentialInsulinModelPreset: InsulinModel {
- var effectDuration: TimeInterval {
- return model.effectDuration
- }
-
- func percentEffectRemaining(at time: TimeInterval) -> Double {
- return model.percentEffectRemaining(at: time)
- }
-}
-
-
-extension ExponentialInsulinModelPreset: CustomDebugStringConvertible {
- var debugDescription: String {
- return "\(self.rawValue)(\(String(reflecting: model))"
- }
-}
diff --git a/Common/Models/LoopSettings.swift b/Common/Models/LoopSettings.swift
deleted file mode 100644
index ae1c5a33b1..0000000000
--- a/Common/Models/LoopSettings.swift
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// LoopSettings.swift
-// Loop
-//
-// Copyright © 2017 LoopKit Authors. All rights reserved.
-//
-
-import LoopKit
-
-
-struct LoopSettings: Equatable {
- var dosingEnabled = false
-
- let dynamicCarbAbsorptionEnabled = true
-
- var glucoseTargetRangeSchedule: GlucoseRangeSchedule?
-
- var maximumBasalRatePerHour: Double?
-
- var maximumBolus: Double?
-
- var suspendThreshold: GlucoseThreshold? = nil
-
- var retrospectiveCorrectionEnabled = true
-
- /// The interval over which to aggregate changes in glucose for retrospective correction
- let retrospectiveCorrectionGroupingInterval = TimeInterval(minutes: 30)
-
- /// The maximum duration over which to integrate retrospective correction changes
- let retrospectiveCorrectionIntegrationInterval = TimeInterval(minutes: 30)
-
- /// The amount of time since a given date that data should be considered valid
- let recencyInterval = TimeInterval(minutes: 15)
-
- // MARK - Display settings
-
- let minimumChartWidthPerHour: CGFloat = 50
-
- let statusChartMinimumHistoryDisplay: TimeInterval = .hours(1)
-}
-
-
-extension LoopSettings: RawRepresentable {
- typealias RawValue = [String: Any]
- private static let version = 1
-
- init?(rawValue: RawValue) {
- guard
- let version = rawValue["version"] as? Int,
- version == LoopSettings.version
- else {
- return nil
- }
-
- if let dosingEnabled = rawValue["dosingEnabled"] as? Bool {
- self.dosingEnabled = dosingEnabled
- }
-
- if let rawValue = rawValue["glucoseTargetRangeSchedule"] as? GlucoseRangeSchedule.RawValue {
- self.glucoseTargetRangeSchedule = GlucoseRangeSchedule(rawValue: rawValue)
- }
-
- self.maximumBasalRatePerHour = rawValue["maximumBasalRatePerHour"] as? Double
-
- self.maximumBolus = rawValue["maximumBolus"] as? Double
-
- if let rawThreshold = rawValue["minimumBGGuard"] as? GlucoseThreshold.RawValue {
- self.suspendThreshold = GlucoseThreshold(rawValue: rawThreshold)
- }
-
- if let retrospectiveCorrectionEnabled = rawValue["retrospectiveCorrectionEnabled"] as? Bool {
- self.retrospectiveCorrectionEnabled = retrospectiveCorrectionEnabled
- }
- }
-
- var rawValue: RawValue {
- var raw: RawValue = [
- "version": LoopSettings.version,
- "dosingEnabled": dosingEnabled,
- "retrospectiveCorrectionEnabled": retrospectiveCorrectionEnabled
- ]
-
- raw["glucoseTargetRangeSchedule"] = glucoseTargetRangeSchedule?.rawValue
- raw["maximumBasalRatePerHour"] = maximumBasalRatePerHour
- raw["maximumBolus"] = maximumBolus
- raw["minimumBGGuard"] = suspendThreshold?.rawValue
-
- return raw
- }
-}
diff --git a/Common/Models/LoopSettingsUserInfo.swift b/Common/Models/LoopSettingsUserInfo.swift
index 06efd35087..a6123825d8 100644
--- a/Common/Models/LoopSettingsUserInfo.swift
+++ b/Common/Models/LoopSettingsUserInfo.swift
@@ -5,6 +5,8 @@
// Copyright © 2018 LoopKit Authors. All rights reserved.
//
+import LoopCore
+
struct LoopSettingsUserInfo {
let settings: LoopSettings
diff --git a/Common/Models/PumpManager.swift b/Common/Models/PumpManager.swift
new file mode 100644
index 0000000000..010bda2459
--- /dev/null
+++ b/Common/Models/PumpManager.swift
@@ -0,0 +1,37 @@
+//
+// PumpManager.swift
+// Loop
+//
+// Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import LoopKit
+import MockKit
+
+public struct AvailableDevice {
+ let identifier: String
+ let localizedTitle: String
+}
+
+
+let staticPumpManagers: [PumpManager.Type] = [
+ MockPumpManager.self,
+]
+
+let staticPumpManagersByIdentifier: [String: PumpManager.Type] = staticPumpManagers.reduce(into: [:]) { (map, Type) in
+ map[Type.managerIdentifier] = Type
+}
+
+let availableStaticPumpManagers = staticPumpManagers.map { (Type) -> AvailableDevice in
+ return AvailableDevice(identifier: Type.managerIdentifier, localizedTitle: Type.localizedTitle)
+}
+
+extension PumpManager {
+ var rawValue: [String: Any] {
+ return [
+ "managerIdentifier": type(of: self).managerIdentifier,
+ "state": self.rawState
+ ]
+ }
+}
diff --git a/Common/Models/PumpManagerUI.swift b/Common/Models/PumpManagerUI.swift
new file mode 100644
index 0000000000..16b602c8e1
--- /dev/null
+++ b/Common/Models/PumpManagerUI.swift
@@ -0,0 +1,35 @@
+//
+// PumpManagerUI.swift
+// Loop
+//
+// Created by Pete Schwamb on 10/18/18.
+// Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import LoopKit
+import LoopKitUI
+
+private let managersByIdentifier: [String: PumpManagerUI.Type] = staticPumpManagers.compactMap{ $0 as? PumpManagerUI.Type}.reduce(into: [:]) { (map, Type) in
+ map[Type.managerIdentifier] = Type
+}
+
+typealias PumpManagerHUDViewsRawValue = [String: Any]
+
+func PumpManagerHUDViewsFromRawValue(_ rawValue: PumpManagerHUDViewsRawValue, pluginManager: PluginManager) -> [BaseHUDView]? {
+ guard
+ let identifier = rawValue["managerIdentifier"] as? String,
+ let rawState = rawValue["hudProviderViews"] as? HUDProvider.HUDViewsRawState,
+ let manager = pluginManager.getPumpManagerTypeByIdentifier(identifier) ?? staticPumpManagersByIdentifier[identifier] as? PumpManagerUI.Type else
+ {
+ return nil
+ }
+ return manager.createHUDViews(rawValue: rawState)
+}
+
+func PumpManagerHUDViewsRawValueFromHUDProvider(_ hudProvider: HUDProvider) -> PumpManagerHUDViewsRawValue {
+ return [
+ "managerIdentifier": hudProvider.managerIdentifier,
+ "hudProviderViews": hudProvider.hudViewsRawState
+ ]
+}
diff --git a/Common/Models/StatusExtensionContext.swift b/Common/Models/StatusExtensionContext.swift
index f385cf30cc..d9ac4aa837 100644
--- a/Common/Models/StatusExtensionContext.swift
+++ b/Common/Models/StatusExtensionContext.swift
@@ -10,13 +10,14 @@
import Foundation
import HealthKit
import LoopKit
+import LoopKitUI
struct NetBasalContext {
let rate: Double
let percentage: Double
let start: Date
- let end: Date
+ let end: Date?
}
struct SensorDisplayableContext: SensorDisplayable {
@@ -26,7 +27,7 @@ struct SensorDisplayableContext: SensorDisplayable {
let isLocal: Bool
}
-struct GlucoseContext {
+struct GlucoseContext: GlucoseValue {
let value: Double
let unit: HKUnit
let startDate: Date
@@ -55,20 +56,20 @@ extension NetBasalContext: RawRepresentable {
typealias RawValue = [String: Any]
var rawValue: RawValue {
- return [
+ var value: RawValue = [
"rate": rate,
"percentage": percentage,
- "start": start,
- "end": end
+ "start": start
]
+ value["end"] = end
+ return value
}
init?(rawValue: RawValue) {
guard
let rate = rawValue["rate"] as? Double,
let percentage = rawValue["percentage"] as? Double,
- let start = rawValue["start"] as? Date,
- let end = rawValue["end"] as? Date
+ let start = rawValue["start"] as? Date
else {
return nil
}
@@ -76,7 +77,7 @@ extension NetBasalContext: RawRepresentable {
self.rate = rate
self.percentage = percentage
self.start = start
- self.end = end
+ self.end = rawValue["end"] as? Date
}
}
@@ -151,6 +152,28 @@ extension PredictedGlucoseContext: RawRepresentable {
}
}
+struct PumpManagerHUDViewsContext: RawRepresentable {
+ typealias RawValue = [String: Any]
+
+ let pumpManagerHUDViewsRawValue: PumpManagerHUDViewsRawValue
+
+ init(pumpManagerHUDViewsRawValue: PumpManagerHUDViewsRawValue) {
+ self.pumpManagerHUDViewsRawValue = pumpManagerHUDViewsRawValue
+ }
+
+ init?(rawValue: RawValue) {
+ if let pumpManagerHUDViewsRawValue = rawValue["pumpManagerHUDViewsRawValue"] as? PumpManagerHUDViewsRawValue {
+ self.pumpManagerHUDViewsRawValue = pumpManagerHUDViewsRawValue
+ } else {
+ return nil
+ }
+ }
+
+ var rawValue: RawValue {
+ return ["pumpManagerHUDViewsRawValue": pumpManagerHUDViewsRawValue]
+ }
+}
+
struct StatusExtensionContext: RawRepresentable {
typealias RawValue = [String: Any]
private let version = 5
@@ -161,6 +184,7 @@ struct StatusExtensionContext: RawRepresentable {
var batteryPercentage: Double?
var reservoirCapacity: Double?
var sensor: SensorDisplayableContext?
+ var pumpManagerHUDViewsContext: PumpManagerHUDViewsContext?
init() { }
@@ -184,6 +208,10 @@ struct StatusExtensionContext: RawRepresentable {
if let rawValue = rawValue["sensor"] as? SensorDisplayableContext.RawValue {
sensor = SensorDisplayableContext(rawValue: rawValue)
}
+
+ if let rawPumpManagerHUDViewsContext = rawValue["pumpManagerHUDViewsContext"] as? PumpManagerHUDViewsContext.RawValue {
+ pumpManagerHUDViewsContext = PumpManagerHUDViewsContext(rawValue: rawPumpManagerHUDViewsContext)
+ }
}
var rawValue: RawValue {
@@ -197,6 +225,8 @@ struct StatusExtensionContext: RawRepresentable {
raw["batteryPercentage"] = batteryPercentage
raw["reservoirCapacity"] = reservoirCapacity
raw["sensor"] = sensor?.rawValue
+ raw["pumpManagerHUDViewsContext"] = pumpManagerHUDViewsContext?.rawValue
+
return raw
}
}
diff --git a/Common/Models/WatchContext.swift b/Common/Models/WatchContext.swift
index d25e5c7e55..67f6c603d4 100644
--- a/Common/Models/WatchContext.swift
+++ b/Common/Models/WatchContext.swift
@@ -11,7 +11,7 @@ import HealthKit
import LoopKit
-final class WatchContext: NSObject, RawRepresentable {
+final class WatchContext: RawRepresentable {
typealias RawValue = [String: Any]
private let version = 4
@@ -32,12 +32,6 @@ final class WatchContext: NSObject, RawRepresentable {
var lastNetTempBasalDate: Date?
var recommendedBolusDose: Double?
- var bolusSuggestion: BolusSuggestionUserInfo? {
- guard let recommended = recommendedBolusDose else { return nil }
-
- return BolusSuggestionUserInfo(recommendedBolus: recommended)
- }
-
var cob: Double?
var iob: Double?
var reservoir: Double?
@@ -46,13 +40,10 @@ final class WatchContext: NSObject, RawRepresentable {
var cgmManagerState: CGMManager.RawStateValue?
- override init() {
- super.init()
+ init() {
}
required init?(rawValue: RawValue) {
- super.init()
-
guard rawValue["v"] as? Int == version else {
return nil
}
diff --git a/Common/Models/WatchPredictedGlucose.swift b/Common/Models/WatchPredictedGlucose.swift
index d801febbfa..080a824074 100644
--- a/Common/Models/WatchPredictedGlucose.swift
+++ b/Common/Models/WatchPredictedGlucose.swift
@@ -11,10 +11,10 @@ import LoopKit
import HealthKit
-struct WatchPredictedGlucose {
- let values: [GlucoseValue]
+struct WatchPredictedGlucose: Equatable {
+ let values: [PredictedGlucoseValue]
- init?(values: [GlucoseValue]) {
+ init?(values: [PredictedGlucoseValue]) {
guard values.count > 1 else {
return nil
}
diff --git a/Common/da.lproj/Intents.strings b/Common/da.lproj/Intents.strings
new file mode 100644
index 0000000000..674ad2a182
--- /dev/null
+++ b/Common/da.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Tilføj kulhydrater";
+
+/* (No Comment) */
+"OcNxIj" = "Tilføj kulhydrater";
+
+/* (No Comment) */
+"yc02Yq" = "Tilføj kulhydrater til Loop.";
+
diff --git a/Common/da.lproj/Localizable.strings b/Common/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..d19cbd7249
--- /dev/null
+++ b/Common/da.lproj/Localizable.strings
@@ -0,0 +1,54 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Tilføj kulhydrater";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Fortsæt";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Modal Dag";
+
+/* Lesson result text for no data */
+"No data available" = "Ingen data tilgængelige";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Interval";
+
+/* Title of config entry */
+"Start Date" = "Start Dato";
+
+/* Lesson title */
+"Time in Range" = "Tme in Range";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualiserer de hyppigste blodsukker værdier fordelt på dagen";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Uger";
+
diff --git a/Common/de.lproj/Intents.strings b/Common/de.lproj/Intents.strings
new file mode 100644
index 0000000000..089a333bfa
--- /dev/null
+++ b/Common/de.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "KH-Eintrag hinzufügen";
+
+/* (No Comment) */
+"OcNxIj" = "KH-Eintrag hinzufügen";
+
+/* (No Comment) */
+"yc02Yq" = "Fügen Sie einen KH-Eintrag zu Loop hinzu.";
+
diff --git a/Common/de.lproj/Localizable.strings b/Common/de.lproj/Localizable.strings
new file mode 100644
index 0000000000..5f6a4f54d8
--- /dev/null
+++ b/Common/de.lproj/Localizable.strings
@@ -0,0 +1,24 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "KH-Eintrag hinzufügen";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "IE";
+
diff --git a/Common/en.lproj/Intents.strings b/Common/en.lproj/Intents.strings
index 504d81b65f..5553d89cde 100644
--- a/Common/en.lproj/Intents.strings
+++ b/Common/en.lproj/Intents.strings
@@ -5,4 +5,4 @@
"OcNxIj" = "Add Carb Entry";
/* INIntentDescription */
-"yc02Yq" = "";
+"yc02Yq" = "Add a carb entry to Loop";
diff --git a/Common/en.lproj/Localizable.strings b/Common/en.lproj/Localizable.strings
new file mode 100644
index 0000000000..e0fb9dff1b
--- /dev/null
+++ b/Common/en.lproj/Localizable.strings
@@ -0,0 +1,24 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Add Carb Entry";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/Common/es.lproj/Intents.strings b/Common/es.lproj/Intents.strings
index 9d5febcec5..a2ede4fc26 100644
--- a/Common/es.lproj/Intents.strings
+++ b/Common/es.lproj/Intents.strings
@@ -1,8 +1,8 @@
/* INIntentTitle */
-"80eo5o" = "Agregar entrada de carbohidratos";
+"80eo5o" = "Agregar Registro de Carbs";
/* INIntentParameterCombinationTitle */
-"OcNxIj" = "Agregar entrada de carbohidratos";
+"OcNxIj" = "Agregar Registro de Carbs";
/* INIntentDescription */
-"yc02Yq" = "";
+"yc02Yq" = "Agregar registro de carbs a Loop";
diff --git a/Common/es.lproj/Localizable.strings b/Common/es.lproj/Localizable.strings
new file mode 100644
index 0000000000..61b37df354
--- /dev/null
+++ b/Common/es.lproj/Localizable.strings
@@ -0,0 +1,23 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Agregar Registro de Carbs";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
diff --git a/Common/fi.lproj/Intents.strings b/Common/fi.lproj/Intents.strings
new file mode 100644
index 0000000000..b51583e515
--- /dev/null
+++ b/Common/fi.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Lisää hiilihydraatteja";
+
+/* (No Comment) */
+"OcNxIj" = "Lisää hiilihydraatteja";
+
+/* (No Comment) */
+"yc02Yq" = "Lisää hiilihydraatteja Loopiin";
+
diff --git a/Common/fi.lproj/Localizable.strings b/Common/fi.lproj/Localizable.strings
new file mode 100644
index 0000000000..4601e886dc
--- /dev/null
+++ b/Common/fi.lproj/Localizable.strings
@@ -0,0 +1,57 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Lisää hiilihydraatteja";
+
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Laskee glukoosimittausten prosenttimäärän määritellyllä alueella";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Jatka";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maksimi";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimi";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Tyypillinen päivä";
+
+/* Lesson result text for no data */
+"No data available" = "Tietoja ei saatavilla";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Alue";
+
+/* Title of config entry */
+"Start Date" = "Aloitusaika";
+
+/* Lesson title */
+"Time in Range" = "Aika tavoitealueella";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Näyttää yleisimmät glukoosiarvot vuorokaudenajan mukaan";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Viikkoa";
+
diff --git a/Common/fr.lproj/Intents.strings b/Common/fr.lproj/Intents.strings
new file mode 100644
index 0000000000..d1a73fa03a
--- /dev/null
+++ b/Common/fr.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Ajouter des glucides";
+
+/* (No Comment) */
+"OcNxIj" = "Ajouter des glucides";
+
+/* (No Comment) */
+"yc02Yq" = "Ajouter des glucides à Loop";
+
diff --git a/Common/fr.lproj/Localizable.strings b/Common/fr.lproj/Localizable.strings
new file mode 100644
index 0000000000..29ab8e8030
--- /dev/null
+++ b/Common/fr.lproj/Localizable.strings
@@ -0,0 +1,24 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Ajouter des glucides";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/Common/it.lproj/Intents.strings b/Common/it.lproj/Intents.strings
index 504d81b65f..7b32eb3aba 100644
--- a/Common/it.lproj/Intents.strings
+++ b/Common/it.lproj/Intents.strings
@@ -1,8 +1,9 @@
-/* INIntentTitle */
-"80eo5o" = "Add Carb Entry";
+/* (No Comment) */
+"80eo5o" = "Aggiungi carboidrati assunti";
-/* INIntentParameterCombinationTitle */
-"OcNxIj" = "Add Carb Entry";
+/* (No Comment) */
+"OcNxIj" = "Aggiungi carboidrati assunti";
+
+/* (No Comment) */
+"yc02Yq" = "Aggiungi carboidrati assunti a Loop";
-/* INIntentDescription */
-"yc02Yq" = "";
diff --git a/Common/it.lproj/Localizable.strings b/Common/it.lproj/Localizable.strings
new file mode 100644
index 0000000000..6da1a77ad0
--- /dev/null
+++ b/Common/it.lproj/Localizable.strings
@@ -0,0 +1,54 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Aggiungi carboidrati assunti";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continua";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Massimo";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimo";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Modalità giornaliera";
+
+/* Lesson result text for no data */
+"No data available" = "Nessun dato disponibile";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Intervallo";
+
+/* Title of config entry */
+"Start Date" = "Data di inizio";
+
+/* Lesson title */
+"Time in Range" = "Tempo nell’intervallo";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualizza i valori di glucosio più frequenti per ora del giorno";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Settimane";
+
diff --git a/Common/ja.lproj/Intents.strings b/Common/ja.lproj/Intents.strings
new file mode 100644
index 0000000000..79c1af0d9d
--- /dev/null
+++ b/Common/ja.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "カーボを追加";
+
+/* (No Comment) */
+"OcNxIj" = "カーボを追加";
+
+/* (No Comment) */
+"yc02Yq" = "ループにカーボを追加";
+
diff --git a/Common/ja.lproj/Localizable.strings b/Common/ja.lproj/Localizable.strings
new file mode 100644
index 0000000000..807227d02a
--- /dev/null
+++ b/Common/ja.lproj/Localizable.strings
@@ -0,0 +1,54 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "カーボを追加";
+
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "指定範囲内の測定値の割合を算出";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "次へ";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "最大";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "最小";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson result text for no data */
+"No data available" = "データがありません";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "範囲";
+
+/* Title of config entry */
+"Start Date" = "開始日";
+
+/* Lesson title */
+"Time in Range" = "タイムインレンジ";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "頻度の高い測定値を時間ごとに表示";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "週";
+
diff --git a/Common/nb.lproj/Intents.strings b/Common/nb.lproj/Intents.strings
new file mode 100644
index 0000000000..494fb5be3c
--- /dev/null
+++ b/Common/nb.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Legg til karbohydrater";
+
+/* (No Comment) */
+"OcNxIj" = "Legg til karbohydrater";
+
+/* (No Comment) */
+"yc02Yq" = "Legg til karbohydrater i Loop";
+
diff --git a/Common/nb.lproj/Localizable.strings b/Common/nb.lproj/Localizable.strings
new file mode 100644
index 0000000000..32bdd8539c
--- /dev/null
+++ b/Common/nb.lproj/Localizable.strings
@@ -0,0 +1,54 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Legg til karbohydrater";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Fortsett";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maksimum";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Modal dag";
+
+/* Lesson result text for no data */
+"No data available" = "Ingen data tilgjengelig";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Målområde";
+
+/* Title of config entry */
+"Start Date" = "Startdato";
+
+/* Lesson title */
+"Time in Range" = "Tid i målområdet";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualiser de nyeste blodsukkerverdier etter tid på døgnet";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Uker";
+
diff --git a/Common/nl.lproj/Intents.strings b/Common/nl.lproj/Intents.strings
new file mode 100644
index 0000000000..86c99c925a
--- /dev/null
+++ b/Common/nl.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Voeg koolhydraten toe";
+
+/* (No Comment) */
+"OcNxIj" = "Voeg koolhydraten toe";
+
+/* (No Comment) */
+"yc02Yq" = "Voeg koolhydraten toe aan Loop";
+
diff --git a/Common/nl.lproj/Localizable.strings b/Common/nl.lproj/Localizable.strings
new file mode 100644
index 0000000000..e98674e351
--- /dev/null
+++ b/Common/nl.lproj/Localizable.strings
@@ -0,0 +1,57 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Voeg koolhydraten toe";
+
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Berekent het percentage glucosemetingen in een specifiek bereik";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Ga verder";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Modale dag";
+
+/* Lesson result text for no data */
+"No data available" = "Geen data beschikbaar";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Bereik";
+
+/* Title of config entry */
+"Start Date" = "Start datum";
+
+/* Lesson title */
+"Time in Range" = "Tijd binnen bereik";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Geeft de meest voorkomende glucose waardes weer per moment van de dag ";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Weken";
+
diff --git a/Common/pl.lproj/Intents.strings b/Common/pl.lproj/Intents.strings
new file mode 100644
index 0000000000..5553d89cde
--- /dev/null
+++ b/Common/pl.lproj/Intents.strings
@@ -0,0 +1,8 @@
+/* INIntentTitle */
+"80eo5o" = "Add Carb Entry";
+
+/* INIntentParameterCombinationTitle */
+"OcNxIj" = "Add Carb Entry";
+
+/* INIntentDescription */
+"yc02Yq" = "Add a carb entry to Loop";
diff --git a/Common/pl.lproj/Localizable.strings b/Common/pl.lproj/Localizable.strings
new file mode 100644
index 0000000000..f630a70c5d
--- /dev/null
+++ b/Common/pl.lproj/Localizable.strings
@@ -0,0 +1,23 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Dodaj pozycję dla węglowodanów";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "J";
diff --git a/Common/pt-BR.lproj/Intents.strings b/Common/pt-BR.lproj/Intents.strings
new file mode 100644
index 0000000000..71d0f90730
--- /dev/null
+++ b/Common/pt-BR.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Adicionar Carboidratos";
+
+/* (No Comment) */
+"OcNxIj" = "Adicionar Carboidratos";
+
+/* (No Comment) */
+"yc02Yq" = "Adicionar Carboidratos ao Loop";
+
diff --git a/Common/pt-BR.lproj/Localizable.strings b/Common/pt-BR.lproj/Localizable.strings
new file mode 100644
index 0000000000..3d70695dd1
--- /dev/null
+++ b/Common/pt-BR.lproj/Localizable.strings
@@ -0,0 +1,54 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Adicionar Carboidratos";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continuar";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Máximo";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Mínimo";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Dia Modal";
+
+/* Lesson result text for no data */
+"No data available" = "Não há dados disponíveis";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Variação";
+
+/* Title of config entry */
+"Start Date" = "Data de Início";
+
+/* Lesson title */
+"Time in Range" = "Tempo na Meta";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualiza os valores de glicose mais frequentes por hora do dia";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Semanas";
+
diff --git a/Common/ro.lproj/Intents.strings b/Common/ro.lproj/Intents.strings
new file mode 100644
index 0000000000..112c6f32d5
--- /dev/null
+++ b/Common/ro.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Adăugare carbohidrați";
+
+/* (No Comment) */
+"OcNxIj" = "Adăugare carbohidrați";
+
+/* (No Comment) */
+"yc02Yq" = "Adaugă carbohidrați în Loop";
+
diff --git a/Common/ro.lproj/Localizable.strings b/Common/ro.lproj/Localizable.strings
new file mode 100644
index 0000000000..13e8f4fd76
--- /dev/null
+++ b/Common/ro.lproj/Localizable.strings
@@ -0,0 +1,57 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Adăugare carbohidrați";
+
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Calculează procentul măsurătorilor glicemice dintr-un interval specificat";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continuă";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maxim";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minim";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Zi modală";
+
+/* Lesson result text for no data */
+"No data available" = "Date inexistente";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Interval";
+
+/* Title of config entry */
+"Start Date" = "Dată inițială";
+
+/* Lesson title */
+"Time in Range" = "Timp petrecut în interval";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Vizualizează cele mai frecvente valori glicemice în funcție de oră";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Săptămâni";
+
diff --git a/Common/ru.lproj/Intents.strings b/Common/ru.lproj/Intents.strings
index 1043009cb4..5ec019a755 100644
--- a/Common/ru.lproj/Intents.strings
+++ b/Common/ru.lproj/Intents.strings
@@ -1,8 +1,9 @@
-/* INIntentTitle */
+/* (No Comment) */
"80eo5o" = "Добавить запись углеводов";
-/* INIntentParameterCombinationTitle */
+/* (No Comment) */
"OcNxIj" = "Добавить запись углеводов";
-/* INIntentDescription */
-"yc02Yq" = "";
+/* (No Comment) */
+"yc02Yq" = "Добавьте запись углеводов в алгоритм цикла";
+
diff --git a/Common/ru.lproj/Localizable.strings b/Common/ru.lproj/Localizable.strings
new file mode 100644
index 0000000000..bdee38ba11
--- /dev/null
+++ b/Common/ru.lproj/Localizable.strings
@@ -0,0 +1,24 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Введите углеводы";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "г";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "мг/дл";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "ммоль/л";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "ед";
+
diff --git a/Common/sv.lproj/Intents.strings b/Common/sv.lproj/Intents.strings
new file mode 100644
index 0000000000..f4ec534f26
--- /dev/null
+++ b/Common/sv.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Lägg till kolhydrater";
+
+/* (No Comment) */
+"OcNxIj" = "Lägg till kolhydrater";
+
+/* (No Comment) */
+"yc02Yq" = "Lägg till kolhydrater för att loopa";
+
diff --git a/Common/sv.lproj/Localizable.strings b/Common/sv.lproj/Localizable.strings
new file mode 100644
index 0000000000..6844bcc037
--- /dev/null
+++ b/Common/sv.lproj/Localizable.strings
@@ -0,0 +1,57 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Lägg till kolhydrater";
+
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Beräknar procentandelen glukosmätningar inom ett specifikt målvärde";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Fortsätt";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Lesson title */
+"Modal Day" = "Genomsnittlig dag";
+
+/* Lesson result text for no data */
+"No data available" = "Ingen data tillgänglig";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "Målvärde";
+
+/* Title of config entry */
+"Start Date" = "Starttid";
+
+/* Lesson title */
+"Time in Range" = "Tid inom målvärde";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visar de vanligaste glukosvärdena under olika tider på dagen";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Veckor";
+
diff --git a/Common/vi.lproj/Intents.strings b/Common/vi.lproj/Intents.strings
new file mode 100644
index 0000000000..5bd02d75d9
--- /dev/null
+++ b/Common/vi.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "Khai báo khối lượng Carb";
+
+/* (No Comment) */
+"OcNxIj" = "Khai báo khối lượng Carb";
+
+/* (No Comment) */
+"yc02Yq" = "Khai báo khối lượng Carb cho Loop";
+
diff --git a/Common/vi.lproj/Localizable.strings b/Common/vi.lproj/Localizable.strings
new file mode 100644
index 0000000000..b97378302c
--- /dev/null
+++ b/Common/vi.lproj/Localizable.strings
@@ -0,0 +1,24 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "Khai báo khối lượng Carb";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/Common/zh-Hans.lproj/Intents.strings b/Common/zh-Hans.lproj/Intents.strings
new file mode 100644
index 0000000000..16f21e6b21
--- /dev/null
+++ b/Common/zh-Hans.lproj/Intents.strings
@@ -0,0 +1,9 @@
+/* (No Comment) */
+"80eo5o" = "添加碳水化合物";
+
+/* (No Comment) */
+"OcNxIj" = "添加碳水化合物";
+
+/* (No Comment) */
+"yc02Yq" = "将碳水化合物添加到Loop";
+
diff --git a/Common/zh-Hans.lproj/Localizable.strings b/Common/zh-Hans.lproj/Localizable.strings
new file mode 100644
index 0000000000..be5965c657
--- /dev/null
+++ b/Common/zh-Hans.lproj/Localizable.strings
@@ -0,0 +1,54 @@
+/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
+"%1$@ v%2$@" = "%1$@ v%2$@";
+
+/* Title of the user activity for adding carbs */
+"Add Carb Entry" = "添加碳水化合物";
+
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "计算在指定范围内的血糖测量值的百分比";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "继续";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "克";
+
+/* Placeholder for upper range entry */
+"Maximum" = "最大";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "毫克/分升";
+
+/* Placeholder for lower range entry */
+"Minimum" = "最小";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "毫摩尔/升";
+
+/* Lesson result text for no data */
+"No data available" = "无数据";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* Section title for glucose range */
+"Range" = "范围";
+
+/* Title of config entry */
+"Start Date" = "开始日期";
+
+/* Lesson title */
+"Time in Range" = "在目标范围的时间";
+
+/* The short unit display string for international units of insulin */
+"U" = "单位";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "全天血糖数据";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "周";
+
diff --git a/Documentation/Testing/Images/mock_managers.png b/Documentation/Testing/Images/mock_managers.png
new file mode 100644
index 0000000000..7c5e7b3dff
Binary files /dev/null and b/Documentation/Testing/Images/mock_managers.png differ
diff --git a/Documentation/Testing/Images/rewind.png b/Documentation/Testing/Images/rewind.png
new file mode 100644
index 0000000000..1bd675e79d
Binary files /dev/null and b/Documentation/Testing/Images/rewind.png differ
diff --git a/Documentation/Testing/Images/scenarios_menu.png b/Documentation/Testing/Images/scenarios_menu.png
new file mode 100644
index 0000000000..604b404de8
Binary files /dev/null and b/Documentation/Testing/Images/scenarios_menu.png differ
diff --git a/Documentation/Testing/Images/scenarios_url.png b/Documentation/Testing/Images/scenarios_url.png
new file mode 100644
index 0000000000..de2024994d
Binary files /dev/null and b/Documentation/Testing/Images/scenarios_url.png differ
diff --git a/Documentation/Testing/Scenarios.md b/Documentation/Testing/Scenarios.md
new file mode 100644
index 0000000000..fabf589f97
--- /dev/null
+++ b/Documentation/Testing/Scenarios.md
@@ -0,0 +1,67 @@
+# Guide: Testing Scenarios
+
+## Purpose
+
+This document describes how to load data-based scenarios, including glucose values, dose history, and carb entries, into Loop on demand.
+
+## File Format
+
+A scenario consists of a single JSON file containing glucose, basal, bolus, and carb entry histories. Each history corresponds to a property of the scenario JSON object—a list of individual entries. Each entry has one or more properties describing its value (e.g. `unitsPerHourValue` and `duration`) and a _relative_ date offset, in seconds (e.g. 0 means 'right now' and -300 means '5 minutes ago').
+
+For example, a carb entry history might look like this:
+
+```json
+"carbEntries": [
+ {
+ "gramValue": 30,
+ "dateOffset": -300,
+ "absorptionTime": 10800
+ },
+ {
+ "gramValue": 15,
+ "dateOffset": 900,
+ "absorptionTime": 7200,
+ "enteredAtOffset": -900
+ }
+]
+```
+
+Carb entries have two date offsets: `dateOffset`, which describes the date at which carbs were consumed, and `enteredAtOffset`, which describes the date at which the carb entry was created. The second carb entry in the example above was entered 30 minutes early.
+
+## Generating Scenarios
+
+A Python script with classes corresponding to the entry types is available at `/Scripts/make_scenario.py`. Running it will generate a sample script, which will allow you to inspect the file format in more detail.
+
+## Loading Scenarios
+
+Launch Loop in the Xcode simulator.
+
+Before loading scenarios, mock pump and CGM managers must be enabled in Loop. From the status screen, tap the settings icon in the bottom-right corner; then, tap on each of the pump and CGM rows and select the Simulator option from the presented action sheets:
+
+
+
+Next, type 'scenario' in the search bar in the bottom-right corner of the Xcode console with the Loop app running:
+
+
+
+The first line will include `[TestingScenariosManager]` and a path to the simulator-specific directory in which to place scenario JSON files.
+
+With one or more scenarios placed in the listed directory, the debug menu can be activated by "shaking" the iPhone: in the simulator, press ^⌘Z. The scenario selection screen will appear:
+
+
+
+Tap on a scenario to select it, then press 'Load' in the top-right corner to load it into Loop.
+
+With the app running, additional scenarios can be added to the scenarios directory; the changes will be detected, and the scenario list reloaded.
+
+## Time Travel
+
+Because all historic date offsets are relative, scenarios can be stepped through one or more loop iterations at a time, so long as the scenario contains sufficient past or future data.
+
+Swiping right or left on a scenario cell reveals the 'rewind' or 'advance' button, respectively:
+
+
+
+Tap on the button, and you will be prompted for a number of loop iterations to progress backward or forward in time. Note that advancing forward will run the full algorithm for each step and in turn apply the suggested basal at each decision point.
+
+For convenience, an active scenario can be stepped through without leaving the status screen. Swipe right or left on the toolbar at the bottom of the screen to move one loop iteration into the past or future, respectively.
diff --git a/DoseMathTests/DoseMathTests.swift b/DoseMathTests/DoseMathTests.swift
index 9b2718701a..f2c1b495cf 100644
--- a/DoseMathTests/DoseMathTests.swift
+++ b/DoseMathTests/DoseMathTests.swift
@@ -9,6 +9,7 @@
import XCTest
import HealthKit
import LoopKit
+import LoopCore
extension XCTestCase {
@@ -54,7 +55,9 @@ class RecommendTempBasalTests: XCTestCase {
fileprivate let maxBasalRate = 3.0
- func loadGlucoseValueFixture(_ resourceName: String) -> [GlucoseValue] {
+ fileprivate let fortyIncrementsPerUnitRounder = { round($0 * 40) / 40 }
+
+ func loadGlucoseValueFixture(_ resourceName: String) -> [GlucoseFixtureValue] {
let fixture: [JSONDictionary] = loadFixture(resourceName)
let dateFormatter = ISO8601DateFormatter.localTimeDateFormatter()
@@ -81,7 +84,7 @@ class RecommendTempBasalTests: XCTestCase {
}
var glucoseTargetRange: GlucoseRangeSchedule {
- return GlucoseRangeSchedule(unit: HKUnit.milligramsPerDeciliter, dailyItems: [RepeatingScheduleValue(startTime: TimeInterval(0), value: DoubleRange(minValue: 90, maxValue: 120))], overrideRanges: [:])!
+ return GlucoseRangeSchedule(unit: HKUnit.milligramsPerDeciliter, dailyItems: [RepeatingScheduleValue(startTime: TimeInterval(0), value: DoubleRange(minValue: 90, maxValue: 120))])!
}
var insulinSensitivitySchedule: InsulinSensitivitySchedule {
@@ -117,6 +120,25 @@ class RecommendTempBasalTests: XCTestCase {
XCTAssertNil(dose)
}
+ func testNoChangeOverrideActive() {
+ let glucose = loadGlucoseValueFixture("recommend_temp_basal_no_change_glucose")
+
+ let dose = glucose.recommendedTempBasal(
+ to: glucoseTargetRange,
+ at: glucose.first!.startDate,
+ suspendThreshold: suspendThreshold.quantity,
+ sensitivity: insulinSensitivitySchedule,
+ model: insulinModel,
+ basalRates: basalRateSchedule,
+ maxBasalRate: maxBasalRate,
+ lastTempBasal: nil,
+ isBasalRateScheduleOverrideActive: true
+ )
+
+ XCTAssertEqual(0.8, dose!.unitsPerHour, accuracy: 1.0 / 40.0)
+ XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)
+ }
+
func testStartHighEndInRange() {
let glucose = loadGlucoseValueFixture("recommend_temp_basal_start_high_end_in_range")
@@ -419,7 +441,7 @@ class RecommendTempBasalTests: XCTestCase {
func testNoInputGlucose() {
- let glucose: [GlucoseValue] = []
+ let glucose: [GlucoseFixtureValue] = []
let dose = glucose.recommendedTempBasal(
to: glucoseTargetRange,
@@ -440,7 +462,9 @@ class RecommendBolusTests: XCTestCase {
fileprivate let maxBolus = 10.0
- func loadGlucoseValueFixture(_ resourceName: String) -> [GlucoseValue] {
+ fileprivate let fortyIncrementsPerUnitRounder = { round($0 * 40) / 40 }
+
+ func loadGlucoseValueFixture(_ resourceName: String) -> [GlucoseFixtureValue] {
let fixture: [JSONDictionary] = loadFixture(resourceName)
let dateFormatter = ISO8601DateFormatter.localTimeDateFormatter()
@@ -467,7 +491,7 @@ class RecommendBolusTests: XCTestCase {
}
var glucoseTargetRange: GlucoseRangeSchedule {
- return GlucoseRangeSchedule(unit: HKUnit.milligramsPerDeciliter, dailyItems: [RepeatingScheduleValue(startTime: TimeInterval(0), value: DoubleRange(minValue: 90, maxValue: 120))], overrideRanges: [:])!
+ return GlucoseRangeSchedule(unit: HKUnit.milligramsPerDeciliter, dailyItems: [RepeatingScheduleValue(startTime: TimeInterval(0), value: DoubleRange(minValue: 90, maxValue: 120))])!
}
var insulinSensitivitySchedule: InsulinSensitivitySchedule {
@@ -560,7 +584,8 @@ class RecommendBolusTests: XCTestCase {
sensitivity: insulinSensitivitySchedule,
model: insulinModel,
pendingInsulin: 0,
- maxBolus: maxBolus
+ maxBolus: maxBolus,
+ volumeRounder: fortyIncrementsPerUnitRounder
)
XCTAssertEqual(1.575, dose.amount)
@@ -628,7 +653,8 @@ class RecommendBolusTests: XCTestCase {
sensitivity: insulinSensitivitySchedule,
model: insulinModel,
pendingInsulin: 0,
- maxBolus: maxBolus
+ maxBolus: maxBolus,
+ volumeRounder: fortyIncrementsPerUnitRounder
)
XCTAssertEqual(1.4, dose.amount)
@@ -646,7 +672,8 @@ class RecommendBolusTests: XCTestCase {
sensitivity: insulinSensitivitySchedule,
model: insulinModel,
pendingInsulin: 1,
- maxBolus: maxBolus
+ maxBolus: maxBolus,
+ volumeRounder: fortyIncrementsPerUnitRounder
)
XCTAssertEqual(0.575, dose.amount)
@@ -740,7 +767,8 @@ class RecommendBolusTests: XCTestCase {
sensitivity: insulinSensitivitySchedule,
model: ExponentialInsulinModel(actionDuration: 21600.0, peakActivityTime: 4500.0),
pendingInsulin: 0,
- maxBolus: maxBolus
+ maxBolus: maxBolus,
+ volumeRounder: fortyIncrementsPerUnitRounder
)
XCTAssertEqual(0.275, dose.amount)
@@ -795,7 +823,7 @@ class RecommendBolusTests: XCTestCase {
func testNoInputGlucose() {
- let glucose: [GlucoseValue] = []
+ let glucose: [GlucoseFixtureValue] = []
let dose = glucose.recommendedBolus(
to: glucoseTargetRange,
diff --git a/DoseMathTests/Info.plist b/DoseMathTests/Info.plist
index 063617a8e3..0be708bea3 100644
--- a/DoseMathTests/Info.plist
+++ b/DoseMathTests/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.9.5
+ $(LOOP_MARKETING_VERSION)
CFBundleSignature
????
CFBundleVersion
diff --git a/DoseMathTests/da.lproj/Localizable.strings b/DoseMathTests/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..13db793a4f
--- /dev/null
+++ b/DoseMathTests/da.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
diff --git a/DoseMathTests/de.lproj/InfoPlist.strings b/DoseMathTests/de.lproj/InfoPlist.strings
deleted file mode 100644
index bbcf8f9040..0000000000
--- a/DoseMathTests/de.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,3 +0,0 @@
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/DoseMathTests/de.lproj/Localizable.strings b/DoseMathTests/de.lproj/Localizable.strings
index 33164c4923..b0c9404b8c 100644
--- a/DoseMathTests/de.lproj/Localizable.strings
+++ b/DoseMathTests/de.lproj/Localizable.strings
@@ -24,3 +24,4 @@
/* The short unit display string for international units of insulin */
"U" = "IE";
+
diff --git a/DoseMathTests/es.lproj/InfoPlist.strings b/DoseMathTests/es.lproj/InfoPlist.strings
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/DoseMathTests/fi.lproj/Localizable.strings b/DoseMathTests/fi.lproj/Localizable.strings
new file mode 100644
index 0000000000..95e33d98bb
--- /dev/null
+++ b/DoseMathTests/fi.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/DoseMathTests/fr.lproj/InfoPlist.strings b/DoseMathTests/fr.lproj/InfoPlist.strings
deleted file mode 100644
index 874e8a4532..0000000000
--- a/DoseMathTests/fr.lproj/InfoPlist.strings
+++ /dev/null
@@ -1 +0,0 @@
-/* No Localized Strings */
diff --git a/DoseMathTests/it.lproj/InfoPlist.strings b/DoseMathTests/it.lproj/InfoPlist.strings
deleted file mode 100644
index bbcf8f9040..0000000000
--- a/DoseMathTests/it.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,3 +0,0 @@
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/DoseMathTests/ja.lproj/Localizable.strings b/DoseMathTests/ja.lproj/Localizable.strings
new file mode 100644
index 0000000000..95e33d98bb
--- /dev/null
+++ b/DoseMathTests/ja.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/DoseMathTests/nb.lproj/InfoPlist.strings b/DoseMathTests/nb.lproj/InfoPlist.strings
deleted file mode 100644
index 874e8a4532..0000000000
--- a/DoseMathTests/nb.lproj/InfoPlist.strings
+++ /dev/null
@@ -1 +0,0 @@
-/* No Localized Strings */
diff --git a/DoseMathTests/nl.lproj/InfoPlist.strings b/DoseMathTests/nl.lproj/InfoPlist.strings
deleted file mode 100644
index 874e8a4532..0000000000
--- a/DoseMathTests/nl.lproj/InfoPlist.strings
+++ /dev/null
@@ -1 +0,0 @@
-/* No Localized Strings */
diff --git a/DoseMathTests/pl.lproj/InfoPlist.strings b/DoseMathTests/pl.lproj/InfoPlist.strings
deleted file mode 100644
index 874e8a4532..0000000000
--- a/DoseMathTests/pl.lproj/InfoPlist.strings
+++ /dev/null
@@ -1 +0,0 @@
-/* No Localized Strings */
diff --git a/DoseMathTests/pt-BR.lproj/Localizable.strings b/DoseMathTests/pt-BR.lproj/Localizable.strings
new file mode 100644
index 0000000000..95e33d98bb
--- /dev/null
+++ b/DoseMathTests/pt-BR.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/DoseMathTests/ro.lproj/Localizable.strings b/DoseMathTests/ro.lproj/Localizable.strings
new file mode 100644
index 0000000000..95e33d98bb
--- /dev/null
+++ b/DoseMathTests/ro.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/DoseMathTests/ru.lproj/InfoPlist.strings b/DoseMathTests/ru.lproj/InfoPlist.strings
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/DoseMathTests/sv.lproj/Localizable.strings b/DoseMathTests/sv.lproj/Localizable.strings
new file mode 100644
index 0000000000..13db793a4f
--- /dev/null
+++ b/DoseMathTests/sv.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
diff --git a/DoseMathTests/vi.lproj/Localizable.strings b/DoseMathTests/vi.lproj/Localizable.strings
new file mode 100644
index 0000000000..95e33d98bb
--- /dev/null
+++ b/DoseMathTests/vi.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* Message when offering bolus recommendation even though bg is below range. (1: glucose value) */
+"Current glucose of %1$@ is below correction range." = "Current glucose of %1$@ is below correction range.";
+
+/* The short unit display string for decibles */
+"dB" = "dB";
+
+/* The short unit display string for grams */
+"g" = "g";
+
+/* The short unit display string for milligrams of glucose per decilter */
+"mg/dL" = "mg/dL";
+
+/* The short unit display string for millimoles of glucose per liter */
+"mmol/L" = "mmol/L";
+
+/* Message when offering bolus recommendation even though bg is below range and minBG is in future. (1: glucose time)(2: glucose number) */
+"Predicted glucose at %1$@ is %2$@." = "Predicted glucose at %1$@ is %2$@.";
+
+/* Notice message when recommending bolus when BG is below the suspend threshold. (1: glucose value) */
+"Predicted glucose of %1$@ is below your suspend threshold setting." = "Predicted glucose of %1$@ is below your suspend threshold setting.";
+
+/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
+"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
+
+/* The short unit display string for international units of insulin */
+"U" = "U";
+
diff --git a/DoseMathTests/zh-Hans.lproj/InfoPlist.strings b/DoseMathTests/zh-Hans.lproj/InfoPlist.strings
deleted file mode 100644
index bbcf8f9040..0000000000
--- a/DoseMathTests/zh-Hans.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,3 +0,0 @@
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/LICENSE.md b/LICENSE.md
index 333c3ab974..15256eda23 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -41,7 +41,7 @@ Copyright (c) 2015 Nathan Racklyeft
Copyright (c) 2016 LoopKit Authors
## RileyLinkKit.framework
-*Including MinimedKit.framework, NightscoutUploadKit.framework, and RileyLinkBLEKit.framework*
+*Including MinimedKit.framework, NightscoutUploadKit.framework, OmniKit.framework, and RileyLinkBLEKit.framework*
Copyright (c) 2015 Pete Schwamb
@@ -58,6 +58,11 @@ Copyright (c) 2016 Mark Wilson
Copyright (c) 2015 Nathan Racklyeft
Copyright (c) 2016 LoopKit Authors
+## MKRingProgressView.framework
+
+Copyright (c) 2015 Max Konovalov
+
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
diff --git a/Learn/AppDelegate.swift b/Learn/AppDelegate.swift
new file mode 100644
index 0000000000..53abb62dbb
--- /dev/null
+++ b/Learn/AppDelegate.swift
@@ -0,0 +1,62 @@
+//
+// AppDelegate.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+
+ guard
+ let nav = window?.rootViewController as? UINavigationController,
+ let lessonsVC = nav.topViewController as? LessonsViewController
+ else {
+ return false
+ }
+
+ let dataManager = DataManager()
+ dataManager.authorize({
+ DispatchQueue.main.async {
+ lessonsVC.lessons = [
+ TimeInRangeLesson(dataManager: dataManager),
+ ModalDayLesson(dataManager: dataManager),
+ ]
+ }
+ })
+
+ return true
+ }
+
+ func applicationWillResignActive(_ application: UIApplication) {
+ // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
+ }
+
+ func applicationDidEnterBackground(_ application: UIApplication) {
+ // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+ }
+
+ func applicationWillEnterForeground(_ application: UIApplication) {
+ // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
+ }
+
+ func applicationDidBecomeActive(_ application: UIApplication) {
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+ }
+
+ func applicationWillTerminate(_ application: UIApplication) {
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+ }
+
+
+}
+
diff --git a/Learn/Assets.xcassets/AppIcon.appiconset/Contents.json b/Learn/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..d8db8d65fd
--- /dev/null
+++ b/Learn/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Loop/Assets.xcassets/Contents.json b/Learn/Assets.xcassets/Contents.json
similarity index 100%
rename from Loop/Assets.xcassets/Contents.json
rename to Learn/Assets.xcassets/Contents.json
diff --git a/Learn/Base.lproj/LaunchScreen.storyboard b/Learn/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000000..bfa3612941
--- /dev/null
+++ b/Learn/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Learn/Base.lproj/Main.storyboard b/Learn/Base.lproj/Main.storyboard
new file mode 100644
index 0000000000..e11dcdf980
--- /dev/null
+++ b/Learn/Base.lproj/Main.storyboard
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Learn/Configuration/DateEntry.swift b/Learn/Configuration/DateEntry.swift
new file mode 100644
index 0000000000..a36a9c5b98
--- /dev/null
+++ b/Learn/Configuration/DateEntry.swift
@@ -0,0 +1,43 @@
+//
+// DateEntry.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import LoopKitUI
+
+
+class DateEntry {
+ private(set) var date: Date
+ let title: String
+ let mode: UIDatePicker.Mode
+
+ init(date: Date, title: String, mode: UIDatePicker.Mode) {
+ self.date = date
+ self.title = title
+ self.mode = mode
+ }
+}
+
+extension DateEntry: LessonCellProviding {
+ func registerCell(for tableView: UITableView) {
+ tableView.register(DateAndDurationTableViewCell.nib(), forCellReuseIdentifier: DateAndDurationTableViewCell.className)
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = tableView.dequeueReusableCell(withIdentifier: DateAndDurationTableViewCell.className, for: indexPath) as! DateAndDurationTableViewCell
+ cell.delegate = self
+ cell.titleLabel.text = title
+ cell.datePicker.isEnabled = true
+ cell.datePicker.datePickerMode = mode
+ cell.date = date
+ return cell
+ }
+}
+
+extension DateEntry: DatePickerTableViewCellDelegate {
+ func datePickerTableViewCellDidUpdateDate(_ cell: DatePickerTableViewCell) {
+ date = cell.datePicker.date
+ }
+}
diff --git a/Learn/Configuration/DateIntervalEntry.swift b/Learn/Configuration/DateIntervalEntry.swift
new file mode 100644
index 0000000000..3763b3bb9e
--- /dev/null
+++ b/Learn/Configuration/DateIntervalEntry.swift
@@ -0,0 +1,52 @@
+//
+// DateIntervalEntry.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+
+
+class DateIntervalEntry: LessonSectionProviding {
+ let headerTitle: String?
+
+ let footerTitle: String?
+
+ let dateEntry: DateEntry
+ let numberEntry: NumberEntry
+
+ let cells: [LessonCellProviding]
+
+ init(headerTitle: String? = nil, footerTitle: String? = nil, start: Date, weeks: Int) {
+ self.headerTitle = headerTitle
+ self.footerTitle = footerTitle
+
+ self.dateEntry = DateEntry(date: start, title: NSLocalizedString("Start Date", comment: "Title of config entry"), mode: .date)
+ self.numberEntry = NumberEntry.integerEntry(value: weeks, unitString: NSLocalizedString("Weeks", comment: "Unit string for a count of calendar weeks"))
+
+ self.cells = [
+ self.dateEntry,
+ self.numberEntry
+ ]
+ }
+}
+
+extension DateIntervalEntry {
+ convenience init(headerTitle: String? = nil, footerTitle: String? = nil, end: Date, weeks: Int, calendar: Calendar = .current) {
+ let start = calendar.date(byAdding: DateComponents(weekOfYear: -weeks), to: end)!
+ self.init(headerTitle: headerTitle, footerTitle: footerTitle, start: calendar.startOfDay(for: start), weeks: weeks)
+ }
+
+ var dateInterval: DateInterval? {
+ let start = dateEntry.date
+
+ guard let weeks = numberEntry.number?.intValue,
+ let end = Calendar.current.date(byAdding: DateComponents(weekOfYear: weeks), to: start)
+ else {
+ return nil
+ }
+
+ return DateInterval(start: start, end: end)
+ }
+}
diff --git a/Learn/Configuration/NumberEntry.swift b/Learn/Configuration/NumberEntry.swift
new file mode 100644
index 0000000000..50eaaa04e4
--- /dev/null
+++ b/Learn/Configuration/NumberEntry.swift
@@ -0,0 +1,110 @@
+//
+// NumberEntry.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import LoopKitUI
+
+
+class TextEntry: TextFieldTableViewCellDelegate {
+
+ func registerCell(for tableView: UITableView) {
+ tableView.register(TextFieldTableViewCell.nib(), forCellReuseIdentifier: TextFieldTableViewCell.className)
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> TextFieldTableViewCell {
+ let cell = tableView.dequeueReusableCell(withIdentifier: TextFieldTableViewCell.className, for: indexPath) as! TextFieldTableViewCell
+ cell.delegate = self
+ return cell
+ }
+
+ // MARK: - TextFieldTableViewCellDelegate
+
+ func textFieldTableViewCellDidBeginEditing(_ cell: TextFieldTableViewCell) {
+
+ }
+
+ func textFieldTableViewCellDidEndEditing(_ cell: TextFieldTableViewCell) {
+
+ }
+}
+
+
+class NumberEntry: TextEntry, LessonCellProviding {
+
+ let formatter: NumberFormatter
+ private(set) var number: NSNumber?
+ let keyboardType: UIKeyboardType
+ let placeholder: String?
+ let unitString: String?
+
+ init(number: NSNumber?, formatter: NumberFormatter, placeholder: String?, unitString: String?, keyboardType: UIKeyboardType) {
+ self.number = number
+ self.formatter = formatter
+ self.placeholder = placeholder
+ self.unitString = unitString
+ self.keyboardType = keyboardType
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = super.tableView(tableView, cellForRowAt: indexPath)
+ cell.textField.placeholder = placeholder
+ cell.unitLabel?.text = unitString
+ cell.textField.keyboardType = keyboardType
+ updateText(for: cell)
+ return cell
+ }
+
+ override func textFieldTableViewCellDidEndEditing(_ cell: TextFieldTableViewCell) {
+ if let text = cell.textField.text {
+ number = formatter.number(from: text)
+ } else {
+ number = nil
+ }
+
+ updateText(for: cell)
+ }
+
+ private func updateText(for cell: TextFieldTableViewCell) {
+ if let number = number {
+ cell.textField.text = formatter.string(from: number)
+ } else {
+ cell.textField.text = nil
+ }
+ }
+}
+
+
+extension NumberEntry {
+ class func decimalEntry(value: Double?, unitString: String?) -> NumberEntry {
+ let number: NSNumber?
+ if let value = value {
+ number = NSNumber(value: value)
+ } else {
+ number = nil
+ }
+
+ let formatter = NumberFormatter()
+ formatter.numberStyle = .decimal
+
+ return NumberEntry(number: number, formatter: formatter, placeholder: nil, unitString: unitString, keyboardType: .decimalPad)
+ }
+
+ class func integerEntry(value: Int?, unitString: String?) -> NumberEntry {
+ let number: NSNumber?
+ if let value = value {
+ number = NSNumber(value: value)
+ } else {
+ number = nil
+ }
+
+
+ let formatter = NumberFormatter()
+ formatter.numberStyle = .none
+
+ return NumberEntry(number: number, formatter: formatter, placeholder: nil, unitString: unitString, keyboardType: .decimalPad)
+ }
+}
diff --git a/Learn/Configuration/NumberRangeEntry.swift b/Learn/Configuration/NumberRangeEntry.swift
new file mode 100644
index 0000000000..8eca26d09c
--- /dev/null
+++ b/Learn/Configuration/NumberRangeEntry.swift
@@ -0,0 +1,64 @@
+//
+// NumberRangeEntry.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+
+
+class NumberRangeEntry: LessonSectionProviding {
+ let headerTitle: String?
+
+ var cells: [LessonCellProviding] {
+ return numberCells
+ }
+
+ var minValue: NSNumber? {
+ return numberCells.compactMap({ $0.number }).min()
+ }
+
+ var maxValue: NSNumber? {
+ return numberCells.compactMap({ $0.number }).max()
+ }
+
+ var range: Range? {
+ guard let minValue = minValue, let maxValue = maxValue else {
+ return nil
+ }
+
+ return minValue..? {
+ guard let minValue = minValue, let maxValue = maxValue else {
+ return nil
+ }
+
+ return minValue...maxValue
+ }
+
+ private var numberCells: [NumberEntry]
+
+ init(headerTitle: String?, minValue: NSNumber?, maxValue: NSNumber?, formatter: NumberFormatter, unitString: String?, keyboardType: UIKeyboardType) {
+ self.headerTitle = headerTitle
+
+ self.numberCells = [
+ NumberEntry(
+ number: minValue,
+ formatter: formatter,
+ placeholder: NSLocalizedString("Minimum", comment: "Placeholder for lower range entry"),
+ unitString: unitString,
+ keyboardType: keyboardType
+ ),
+ NumberEntry(
+ number: maxValue,
+ formatter: formatter,
+ placeholder: NSLocalizedString("Maximum", comment: "Placeholder for upper range entry"),
+ unitString: unitString,
+ keyboardType: keyboardType
+ ),
+ ]
+ }
+}
diff --git a/Learn/Configuration/QuantityRangeEntry.swift b/Learn/Configuration/QuantityRangeEntry.swift
new file mode 100644
index 0000000000..abb7ac344e
--- /dev/null
+++ b/Learn/Configuration/QuantityRangeEntry.swift
@@ -0,0 +1,88 @@
+//
+// QuantityRangeEntry.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import HealthKit
+import LoopKit
+import UIKit
+
+
+class QuantityRangeEntry: LessonSectionProviding {
+ var headerTitle: String? {
+ return numberRange.headerTitle
+ }
+
+ var footerTitle: String? {
+ return numberRange.footerTitle
+ }
+
+ var cells: [LessonCellProviding] {
+ return numberRange.cells
+ }
+
+ var minValue: HKQuantity? {
+ if let minValue = numberRange.minValue?.doubleValue {
+ return HKQuantity(unit: unit, doubleValue: minValue)
+ } else {
+ return nil
+ }
+ }
+
+ var maxValue: HKQuantity? {
+ if let maxValue = numberRange.maxValue?.doubleValue {
+ return HKQuantity(unit: unit, doubleValue: maxValue)
+ } else {
+ return nil
+ }
+ }
+
+ var range: Range? {
+ guard let minValue = minValue, let maxValue = maxValue else {
+ return nil
+ }
+
+ return minValue..? {
+ guard let minValue = minValue, let maxValue = maxValue else {
+ return nil
+ }
+
+ return minValue...maxValue
+ }
+
+ private let numberRange: NumberRangeEntry
+
+ let quantityFormatter: QuantityFormatter
+
+ let unit: HKUnit
+
+ init(headerTitle: String?, minValue: HKQuantity?, maxValue: HKQuantity?, quantityFormatter: QuantityFormatter, unit: HKUnit, keyboardType: UIKeyboardType) {
+ self.quantityFormatter = quantityFormatter
+ self.unit = unit
+
+ numberRange = NumberRangeEntry(
+ headerTitle: headerTitle,
+ minValue: (minValue?.doubleValue(for: unit)).map(NSNumber.init(value:)),
+ maxValue: (maxValue?.doubleValue(for: unit)).map(NSNumber.init(value:)),
+ formatter: quantityFormatter.numberFormatter,
+ unitString: quantityFormatter.string(from: unit),
+ keyboardType: keyboardType
+ )
+ }
+
+ internal class func glucoseRange(minValue: HKQuantity, maxValue: HKQuantity, quantityFormatter: QuantityFormatter, unit: HKUnit) -> QuantityRangeEntry {
+ return QuantityRangeEntry(
+ headerTitle: NSLocalizedString("Range", comment: "Section title for glucose range"),
+ minValue: minValue,
+ maxValue: maxValue,
+ quantityFormatter: quantityFormatter,
+ unit: unit,
+ keyboardType: unit == .milligramsPerDeciliter ? .numberPad : .decimalPad
+ )
+ }
+}
diff --git a/Learn/Display/DatesAndNumberCell.swift b/Learn/Display/DatesAndNumberCell.swift
new file mode 100644
index 0000000000..be663f716a
--- /dev/null
+++ b/Learn/Display/DatesAndNumberCell.swift
@@ -0,0 +1,43 @@
+//
+// DatesAndNumberCell.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+
+
+class DatesAndNumberCell: LessonCellProviding {
+ static let cellIdentifier = "DatesAndNumberCell"
+
+ let date: DateInterval
+ let value: NSNumber
+ let dateFormatter: DateIntervalFormatter
+ let numberFormatter: NumberFormatter
+
+ init(date: DateInterval, value: NSNumber, dateFormatter: DateIntervalFormatter, numberFormatter: NumberFormatter) {
+ self.date = date
+ self.value = value
+ self.dateFormatter = dateFormatter
+ self.numberFormatter = numberFormatter
+ }
+
+ func registerCell(for tableView: UITableView) {
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell: UITableViewCell
+
+ if let existingCell = tableView.dequeueReusableCell(withIdentifier: DatesAndNumberCell.cellIdentifier) {
+ cell = existingCell
+ } else {
+ cell = UITableViewCell(style: .value1, reuseIdentifier: DatesAndNumberCell.cellIdentifier)
+ }
+
+ cell.textLabel?.text = dateFormatter.string(from: date)
+ cell.detailTextLabel?.text = numberFormatter.string(from: value)
+
+ return cell
+ }
+}
diff --git a/Learn/Display/TextCell.swift b/Learn/Display/TextCell.swift
new file mode 100644
index 0000000000..1b96291536
--- /dev/null
+++ b/Learn/Display/TextCell.swift
@@ -0,0 +1,51 @@
+//
+// TextCell.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+
+
+class TextCell: LessonCellProviding {
+ static let cellIdentifier = "TextCell"
+
+ let text: String
+ let detailText: String?
+ let textColor: UIColor?
+ let detailTextColor: UIColor?
+
+ init(text: String, detailText: String? = nil, textColor: UIColor? = nil, detailTextColor: UIColor? = nil) {
+ self.text = text
+ self.detailText = detailText
+ self.textColor = textColor
+ self.detailTextColor = detailTextColor
+ }
+
+ func registerCell(for tableView: UITableView) {
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell: UITableViewCell
+
+ if let existingCell = tableView.dequeueReusableCell(withIdentifier: TextCell.cellIdentifier) {
+ cell = existingCell
+ } else {
+ cell = UITableViewCell(style: .value1, reuseIdentifier: TextCell.cellIdentifier)
+ }
+
+ cell.textLabel?.text = text
+ cell.detailTextLabel?.text = detailText
+
+ if let color = textColor {
+ cell.textLabel?.textColor = color
+ }
+
+ if let color = detailTextColor {
+ cell.detailTextLabel?.textColor = color
+ }
+
+ return cell
+ }
+}
diff --git a/Learn/Extensions/Date.swift b/Learn/Extensions/Date.swift
new file mode 100644
index 0000000000..bd2f73b890
--- /dev/null
+++ b/Learn/Extensions/Date.swift
@@ -0,0 +1,21 @@
+//
+// Date.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+extension Date: Strideable {
+ public typealias Stride = TimeInterval
+
+ public func distance(to other: Date) -> TimeInterval {
+ return other.timeIntervalSince(self)
+ }
+
+ public func advanced(by n: TimeInterval) -> Date {
+ return addingTimeInterval(n)
+ }
+}
diff --git a/Learn/Extensions/DateIntervalFormatter.swift b/Learn/Extensions/DateIntervalFormatter.swift
new file mode 100644
index 0000000000..fe2ed19009
--- /dev/null
+++ b/Learn/Extensions/DateIntervalFormatter.swift
@@ -0,0 +1,17 @@
+//
+// DateIntervalFormatter.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+extension DateIntervalFormatter {
+ convenience init(dateStyle: DateIntervalFormatter.Style = .none, timeStyle: DateIntervalFormatter.Style = .none) {
+ self.init()
+ self.dateStyle = dateStyle
+ self.timeStyle = timeStyle
+ }
+}
diff --git a/Learn/Extensions/NSNumber.swift b/Learn/Extensions/NSNumber.swift
new file mode 100644
index 0000000000..4557f6f805
--- /dev/null
+++ b/Learn/Extensions/NSNumber.swift
@@ -0,0 +1,15 @@
+//
+// NSNumber.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+extension NSNumber: Comparable {
+ public static func < (lhs: NSNumber, rhs: NSNumber) -> Bool {
+ return lhs.compare(rhs) == .orderedAscending
+ }
+}
diff --git a/Learn/Extensions/OSLog.swift b/Learn/Extensions/OSLog.swift
new file mode 100644
index 0000000000..0e2001d042
--- /dev/null
+++ b/Learn/Extensions/OSLog.swift
@@ -0,0 +1,15 @@
+//
+// OSLog.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import os.log
+
+
+extension OSLog {
+ convenience init(category: String) {
+ self.init(subsystem: "com.loopkit.Learn", category: category)
+ }
+}
diff --git a/Learn/Extensions/Sequence.swift b/Learn/Extensions/Sequence.swift
new file mode 100644
index 0000000000..c3913cd474
--- /dev/null
+++ b/Learn/Extensions/Sequence.swift
@@ -0,0 +1,34 @@
+//
+// Sequence.swift
+// Learn
+//
+// Created by Pete Schwamb on 4/10/19.
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+extension Sequence {
+ func proportion(where isIncluded: (Element) -> Bool) -> Double? {
+ return average(by: { isIncluded($0) ? 1 : 0 })
+ }
+
+ func average(by getMetric: (Element) -> T) -> T? {
+ let (sum, count) = reduce(into: (sum: 0 as T, count: 0)) { result, element in
+ result.0 += getMetric(element)
+ result.1 += 1
+ }
+
+ guard count > 0 else {
+ return nil
+ }
+
+ return sum / T(count)
+ }
+}
+
+extension Sequence where Element: FloatingPoint {
+ func average() -> Element? {
+ return average(by: { $0 })
+ }
+}
diff --git a/Learn/Extensions/UITableViewCell.swift b/Learn/Extensions/UITableViewCell.swift
new file mode 100644
index 0000000000..369855d0f6
--- /dev/null
+++ b/Learn/Extensions/UITableViewCell.swift
@@ -0,0 +1,16 @@
+//
+// UITableViewCell.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import LoopCore
+import LoopKitUI
+import LoopUI
+
+
+extension DateAndDurationTableViewCell: NibLoadable { }
+
+
+extension TextButtonTableViewCell: IdentifiableClass { }
diff --git a/Learn/Info.plist b/Learn/Info.plist
new file mode 100644
index 0000000000..cd88cba1d1
--- /dev/null
+++ b/Learn/Info.plist
@@ -0,0 +1,53 @@
+
+
+
+
+ MainAppBundleIdentifier
+ $(MAIN_APP_BUNDLE_IDENTIFIER)
+ AppGroupIdentifier
+ $(APP_GROUP_IDENTIFIER)
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(LOOP_MARKETING_VERSION)
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS
+
+ NSHealthShareUsageDescription
+ Meal data from the Health database is used to determine glucose effects. Glucose data from the Health database is used for graphing and momentum calculation.
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+ healthkit
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+ UIInterfaceOrientationPortraitUpsideDown
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/Learn/Learn.entitlements b/Learn/Learn.entitlements
new file mode 100644
index 0000000000..b09682c70b
--- /dev/null
+++ b/Learn/Learn.entitlements
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.developer.healthkit
+
+ com.apple.developer.healthkit.access
+
+ com.apple.security.application-groups
+
+ $(APP_GROUP_IDENTIFIER)
+
+
+
diff --git a/Learn/Lesson.swift b/Learn/Lesson.swift
new file mode 100644
index 0000000000..35c56a2ee1
--- /dev/null
+++ b/Learn/Lesson.swift
@@ -0,0 +1,63 @@
+//
+// Lesson.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+
+protocol Lesson {
+ init(dataManager: DataManager)
+
+ var title: String { get }
+
+ var subtitle: String { get }
+
+ var configurationSections: [LessonSectionProviding] { get }
+
+ func execute(completion: @escaping (_ resultSections: [LessonSectionProviding]) -> Void)
+}
+
+
+protocol LessonSectionProviding {
+ var headerTitle: String? { get }
+
+ var footerTitle: String? { get }
+
+ var cells: [LessonCellProviding] { get }
+}
+
+extension LessonSectionProviding {
+ var headerTitle: String? {
+ return nil
+ }
+
+ var footerTitle: String? {
+ return nil
+ }
+}
+
+
+protocol LessonCellProviding {
+ func registerCell(for tableView: UITableView)
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
+}
+
+
+struct LessonSection: LessonSectionProviding {
+ let headerTitle: String?
+
+ let footerTitle: String?
+
+ let cells: [LessonCellProviding]
+
+ init(headerTitle: String? = nil, footerTitle: String? = nil, cells: [LessonCellProviding]) {
+ self.headerTitle = headerTitle
+ self.footerTitle = footerTitle
+ self.cells = cells
+ }
+}
diff --git a/Learn/Lessons/ModalDayLesson.swift b/Learn/Lessons/ModalDayLesson.swift
new file mode 100644
index 0000000000..751ea3a471
--- /dev/null
+++ b/Learn/Lessons/ModalDayLesson.swift
@@ -0,0 +1,212 @@
+//
+// ModalDayLesson.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import HealthKit
+import LoopCore
+import LoopKit
+import os.log
+
+final class ModalDayLesson: Lesson {
+ let title = NSLocalizedString("Modal Day", comment: "Lesson title")
+
+ let subtitle = NSLocalizedString("Visualizes the most frequent glucose values by time of day", comment: "Lesson subtitle")
+
+ let configurationSections: [LessonSectionProviding]
+
+ private let dataManager: DataManager
+
+ private let dateIntervalEntry: DateIntervalEntry
+
+ private let glucoseUnit: HKUnit
+
+ init(dataManager: DataManager) {
+ self.dataManager = dataManager
+ self.glucoseUnit = dataManager.glucoseStore.preferredUnit ?? .milligramsPerDeciliter
+
+ dateIntervalEntry = DateIntervalEntry(
+ end: Date(),
+ weeks: 2
+ )
+
+ self.configurationSections = [
+ dateIntervalEntry
+ ]
+ }
+
+ func execute(completion: @escaping ([LessonSectionProviding]) -> Void) {
+ guard let dates = dateIntervalEntry.dateInterval else {
+ // TODO: Cleaner error presentation
+ completion([LessonSection(headerTitle: "Error: Please fill out all fields", footerTitle: nil, cells: [])])
+ return
+ }
+
+ let calendar = Calendar.current
+
+ let calculator = ModalDayCalculator(dataManager: dataManager, dates: dates, bucketSize: .minutes(60), unit: glucoseUnit, calendar: calendar)
+ calculator.execute { (result) in
+ switch result {
+ case .failure(let error):
+ completion([
+ LessonSection(cells: [TextCell(text: String(describing: error))])
+ ])
+ case .success(let buckets):
+ guard buckets.count > 0 else {
+ completion([
+ LessonSection(cells: [TextCell(text: NSLocalizedString("No data available", comment: "Lesson result text for no data"))])
+ ])
+ return
+ }
+
+ let dateFormatter = DateIntervalFormatter(timeStyle: .short)
+ let glucoseFormatter = QuantityFormatter()
+ glucoseFormatter.setPreferredNumberFormatter(for: self.glucoseUnit)
+
+ completion([
+ LessonSection(cells: buckets.compactMap({ (bucket) -> TextCell? in
+ guard let start = calendar.date(from: bucket.time.lowerBound.dateComponents),
+ let end = calendar.date(from: bucket.time.upperBound.dateComponents),
+ let time = dateFormatter.string(from: DateInterval(start: start, end: end)),
+ let median = bucket.median,
+ let medianString = glucoseFormatter.string(from: median, for: bucket.unit)
+ else {
+ return nil
+ }
+
+ return TextCell(text: time, detailText: medianString)
+ }))
+ ])
+ }
+ }
+ }
+}
+
+
+fileprivate extension TextCell {
+
+}
+
+
+fileprivate struct ModalDayBucket {
+ let time: Range
+ let orderedValues: [Double]
+ let unit: HKUnit
+
+ init(time: Range, unorderedValues: [Double], unit: HKUnit) {
+ self.time = time
+ self.orderedValues = unorderedValues.sorted()
+ self.unit = unit
+ }
+
+ var median: HKQuantity? {
+ let count = orderedValues.count
+ guard count > 0 else {
+ return nil
+ }
+
+ if count % 2 == 1 {
+ return HKQuantity(unit: unit, doubleValue: orderedValues[count / 2])
+ } else {
+ let mid = count / 2
+ let lower = orderedValues[mid - 1]
+ let upper = orderedValues[mid]
+ return HKQuantity(unit: unit, doubleValue: (lower + upper) / 2)
+ }
+ }
+}
+
+
+fileprivate struct ModalDayBuilder {
+ let calendar: Calendar
+ let bucketSize: TimeInterval
+ let unit: HKUnit
+ private(set) var unorderedValuesByBucket: [Range: [Double]]
+
+ init(calendar: Calendar, bucketSize: TimeInterval, unit: HKUnit) {
+ self.calendar = calendar
+ self.bucketSize = bucketSize
+ self.unit = unit
+ self.unorderedValuesByBucket = [:]
+ }
+
+ mutating func add(_ value: Double, at time: TimeComponents) {
+ let bucket = time.bucket(withBucketSize: bucketSize)
+ var values = unorderedValuesByBucket[bucket] ?? []
+ values.append(value)
+ unorderedValuesByBucket[bucket] = values
+ }
+
+ mutating func add(_ value: Double, at date: DateComponents) {
+ guard let time = TimeComponents(dateComponents: date) else {
+ return
+ }
+ add(value, at: time)
+ }
+
+ mutating func add(_ value: Double, at date: Date) {
+ add(value, at: calendar.dateComponents([.hour, .minute], from: date))
+ }
+
+ mutating func add(_ quantity: HKQuantity, at date: Date) {
+ add(quantity.doubleValue(for: unit), at: date)
+ }
+
+ var allBuckets: [ModalDayBucket] {
+ return unorderedValuesByBucket.sorted(by: { $0.0.lowerBound < $1.0.lowerBound }).map { pair -> ModalDayBucket in
+ return ModalDayBucket(time: pair.key, unorderedValues: pair.value, unit: unit)
+ }
+ }
+}
+
+
+fileprivate class ModalDayCalculator {
+ typealias ResultType = ModalDayBuilder
+ let calculator: DayCalculator
+ let bucketSize: TimeInterval
+ let calendar: Calendar
+ private let log: OSLog
+
+ init(dataManager: DataManager, dates: DateInterval, bucketSize: TimeInterval, unit: HKUnit, calendar: Calendar) {
+ self.calculator = DayCalculator(dataManager: dataManager, dates: dates, initial: ModalDayBuilder(calendar: calendar, bucketSize: bucketSize, unit: unit))
+ self.bucketSize = bucketSize
+ self.calendar = calendar
+
+ log = OSLog(category: String(describing: type(of: self)))
+ }
+
+ func execute(completion: @escaping (_ result: Result<[ModalDayBucket]>) -> Void) {
+ os_log(.default, log: log, "Computing Modal day in %{public}@", String(describing: calculator.dates))
+
+ calculator.execute(calculator: { (dataManager, day, mutableResult, completion) in
+ os_log(.default, log: self.log, "Fetching samples in %{public}@", String(describing: day))
+
+ dataManager.glucoseStore.getGlucoseSamples(start: day.start, end: day.end, completion: { (result) in
+ switch result {
+ case .failure(let error):
+ os_log(.error, log: self.log, "Failed to fetch samples: %{public}@", String(describing: error))
+ completion(error)
+ case .success(let samples):
+ os_log(.error, log: self.log, "Found %d samples", samples.count)
+
+ for sample in samples {
+ _ = mutableResult.mutate({ (result) in
+ result.add(sample.quantity, at: sample.startDate)
+ })
+ }
+ completion(nil)
+ }
+ })
+ }, completion: { (result) in
+ switch result {
+ case .failure(let error):
+ completion(.failure(error))
+ case .success(let builder):
+ completion(.success(builder.allBuckets))
+ }
+ })
+ }
+}
diff --git a/Learn/Lessons/TimeInRangeLesson.swift b/Learn/Lessons/TimeInRangeLesson.swift
new file mode 100644
index 0000000000..0d3ac7cf8e
--- /dev/null
+++ b/Learn/Lessons/TimeInRangeLesson.swift
@@ -0,0 +1,190 @@
+//
+// LessonPlayground.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import LoopCore
+import LoopKit
+import LoopKitUI
+import LoopUI
+import HealthKit
+import os.log
+
+
+final class TimeInRangeLesson: Lesson {
+ let title = NSLocalizedString("Time in Range", comment: "Lesson title")
+
+ let subtitle = NSLocalizedString("Computes the percentage of glucose measurements within a specified range", comment: "Lesson subtitle")
+
+ let configurationSections: [LessonSectionProviding]
+
+ private let dataManager: DataManager
+
+ private let glucoseUnit: HKUnit
+
+ private let glucoseFormatter = QuantityFormatter()
+
+ private let dateIntervalEntry: DateIntervalEntry
+
+ private let rangeEntry: QuantityRangeEntry
+
+ init(dataManager: DataManager) {
+ self.dataManager = dataManager
+ self.glucoseUnit = dataManager.glucoseStore.preferredUnit ?? .milligramsPerDeciliter
+
+ glucoseFormatter.setPreferredNumberFormatter(for: glucoseUnit)
+
+ dateIntervalEntry = DateIntervalEntry(
+ end: Date(),
+ weeks: 2
+ )
+
+ rangeEntry = QuantityRangeEntry.glucoseRange(
+ minValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 80),
+ maxValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 160),
+ quantityFormatter: glucoseFormatter,
+ unit: glucoseUnit)
+
+ self.configurationSections = [
+ dateIntervalEntry,
+ rangeEntry
+ ]
+ }
+
+ func execute(completion: @escaping ([LessonSectionProviding]) -> Void) {
+ guard let dates = dateIntervalEntry.dateInterval, let closedRange = rangeEntry.closedRange else {
+ // TODO: Cleaner error presentation
+ completion([LessonSection(headerTitle: "Error: Please fill out all fields", footerTitle: nil, cells: [])])
+ return
+ }
+
+ let calculator = TimeInRangeCalculator(dataManager: dataManager, dates: dates, range: closedRange)
+
+ calculator.execute { result in
+ switch result {
+ case .failure(let error):
+ completion([
+ LessonSection(cells: [TextCell(text: String(describing: error))])
+ ])
+ case .success(let resultsByDay):
+ guard resultsByDay.count > 0 else {
+ completion([
+ LessonSection(cells: [TextCell(text: NSLocalizedString("No data available", comment: "Lesson result text for no data"))])
+ ])
+ return
+ }
+
+ let dateFormatter = DateIntervalFormatter(dateStyle: .short, timeStyle: .none)
+ let numberFormatter = NumberFormatter()
+ numberFormatter.numberStyle = .percent
+
+ var aggregator = TimeInRangeAggregator()
+ resultsByDay.forEach({ (pair) in
+ aggregator.add(percentInRange: pair.value, for: pair.key)
+ })
+
+ completion([
+ TimesInRangeSection(
+ ranges: aggregator.results.map { [$0.range:$0.value] } ?? [:],
+ dateFormatter: dateFormatter,
+ numberFormatter: numberFormatter
+ ),
+ TimesInRangeSection(
+ ranges: resultsByDay,
+ dateFormatter: dateFormatter,
+ numberFormatter: numberFormatter
+ )
+ ])
+ }
+ }
+ }
+}
+
+class TimesInRangeSection: LessonSectionProviding {
+
+ let cells: [LessonCellProviding]
+
+ init(ranges: [DateInterval: Double], dateFormatter: DateIntervalFormatter, numberFormatter: NumberFormatter) {
+ cells = ranges.sorted(by: { $0.0 < $1.0 }).map { pair -> LessonCellProviding in
+ DatesAndNumberCell(date: pair.key, value: NSNumber(value: pair.value), dateFormatter: dateFormatter, numberFormatter: numberFormatter)
+ }
+ }
+}
+
+
+struct TimeInRangeAggregator {
+ private var count = 0
+ private var sum: Double = 0
+ var allDates: DateInterval?
+
+ var averagePercentInRange: Double? {
+ guard count > 0 else {
+ return nil
+ }
+
+ return sum / Double(count)
+ }
+
+ var results: (range: DateInterval, value: Double)? {
+ guard let allDates = allDates, let averagePercentInRange = averagePercentInRange else {
+ return nil
+ }
+
+ return (range: allDates, value: averagePercentInRange)
+ }
+
+ mutating func add(percentInRange: Double, for dates: DateInterval) {
+ sum += percentInRange
+ count += 1
+
+ if let allDates = self.allDates {
+ self.allDates = DateInterval(start: min(allDates.start, dates.start), end: max(allDates.end, dates.end))
+ } else {
+ self.allDates = dates
+ }
+ }
+}
+
+
+/// Time-in-range, e.g. "2 weeks starting on March 5"
+private class TimeInRangeCalculator {
+ let calculator: DayCalculator<[DateInterval: Double]>
+ let range: ClosedRange
+
+ private let log: OSLog
+
+ private let unit = HKUnit.milligramsPerDeciliter
+
+ init(dataManager: DataManager, dates: DateInterval, range: ClosedRange) {
+ self.calculator = DayCalculator(dataManager: dataManager, dates: dates, initial: [:])
+ self.range = range
+
+ log = OSLog(category: String(describing: type(of: self)))
+ }
+
+ func execute(completion: @escaping (_ result: Result<[DateInterval: Double]>) -> Void) {
+ os_log(.default, log: log, "Computing Time in range from %{public}@ between %{public}@", String(describing: calculator.dates), String(describing: range))
+
+ calculator.execute(calculator: { (dataManager, day, results, completion) in
+ os_log(.default, log: self.log, "Fetching samples in %{public}@", String(describing: day))
+
+ dataManager.glucoseStore.getGlucoseSamples(start: day.start, end: day.end) { (result) in
+ switch result {
+ case .failure(let error):
+ os_log(.error, log: self.log, "Failed to fetch samples: %{public}@", String(describing: error))
+ completion(error)
+ case .success(let samples):
+ if let timeInRange = samples.proportion(where: { self.range.contains($0.quantity) }) {
+ _ = results.mutate({ (results) in
+ results[day] = timeInRange
+ })
+ }
+ completion(nil)
+ }
+ }
+ }, completion: completion)
+ }
+}
diff --git a/Learn/Managers/DataManager.swift b/Learn/Managers/DataManager.swift
new file mode 100644
index 0000000000..7082b5e6ff
--- /dev/null
+++ b/Learn/Managers/DataManager.swift
@@ -0,0 +1,133 @@
+//
+// DataManager.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import HealthKit
+import LoopKit
+import LoopCore
+
+
+final class DataManager {
+ let carbStore: CarbStore
+
+ let doseStore: DoseStore
+
+ let glucoseStore: GlucoseStore
+
+ let settings: LoopSettings
+
+ init(
+ basalRateSchedule: BasalRateSchedule? = UserDefaults.appGroup?.basalRateSchedule,
+ carbRatioSchedule: CarbRatioSchedule? = UserDefaults.appGroup?.carbRatioSchedule,
+ insulinModelSettings: InsulinModelSettings? = UserDefaults.appGroup?.insulinModelSettings,
+ insulinSensitivitySchedule: InsulinSensitivitySchedule? = UserDefaults.appGroup?.insulinSensitivitySchedule,
+ settings: LoopSettings = UserDefaults.appGroup?.loopSettings ?? LoopSettings()
+ ) {
+ self.settings = settings
+
+ let healthStore = HKHealthStore()
+ let cacheStore = PersistenceController.controllerInAppGroupDirectory(isReadOnly: true)
+
+ carbStore = CarbStore(
+ healthStore: healthStore,
+ cacheStore: cacheStore,
+ observationEnabled: false,
+ carbRatioSchedule: carbRatioSchedule,
+ insulinSensitivitySchedule: insulinSensitivitySchedule
+ )
+
+ doseStore = DoseStore(
+ healthStore: healthStore,
+ cacheStore: cacheStore,
+ observationEnabled: false,
+ insulinModel: insulinModelSettings?.model,
+ basalProfile: basalRateSchedule,
+ insulinSensitivitySchedule: insulinSensitivitySchedule
+ )
+
+ glucoseStore = GlucoseStore(
+ healthStore: healthStore,
+ cacheStore: cacheStore,
+ observationEnabled: false
+ )
+ }
+}
+
+
+// MARK: - Thread-safe Preferences
+extension DataManager {
+ /// The daily schedule of basal insulin rates
+ var basalRateSchedule: BasalRateSchedule? {
+ return doseStore.basalProfile
+ }
+
+ /// The daily schedule of carbs-to-insulin ratios
+ /// This is measured in grams/Unit
+ var carbRatioSchedule: CarbRatioSchedule? {
+ return carbStore.carbRatioSchedule
+ }
+
+ /// The length of time insulin has an effect on blood glucose
+ var insulinModelSettings: InsulinModelSettings? {
+ guard let model = doseStore.insulinModel else {
+ return nil
+ }
+
+ return InsulinModelSettings(model: model)
+ }
+
+ /// The daily schedule of insulin sensitivity (also known as ISF)
+ /// This is measured in /Unit
+ var insulinSensitivitySchedule: InsulinSensitivitySchedule? {
+ return carbStore.insulinSensitivitySchedule
+ }
+}
+
+
+// MARK: - HealthKit Setup
+extension DataManager {
+ var healthStore: HKHealthStore {
+ return carbStore.healthStore
+ }
+
+ /// All the HealthKit types to be read and shared by stores
+ private var sampleTypes: Set {
+ return Set([
+ glucoseStore.sampleType,
+ carbStore.sampleType,
+ doseStore.sampleType,
+ ].compactMap { $0 })
+ }
+
+ /// True if any stores require HealthKit authorization
+ var authorizationRequired: Bool {
+ return glucoseStore.authorizationRequired ||
+ carbStore.authorizationRequired ||
+ doseStore.authorizationRequired
+ }
+
+ /// True if the user has explicitly denied access to any stores' HealthKit types
+ private var sharingDenied: Bool {
+ return glucoseStore.sharingDenied ||
+ carbStore.sharingDenied ||
+ doseStore.sharingDenied
+ }
+
+ func authorize(_ completion: @escaping () -> Void) {
+ // Authorize all types at once for simplicity
+ carbStore.healthStore.requestAuthorization(toShare: [], read: sampleTypes) { (success, error) in
+ if success {
+ // Call the individual authorization methods to trigger query creation
+ self.carbStore.authorize(toShare: false, { _ in })
+ self.doseStore.insulinDeliveryStore.authorize(toShare: false, { _ in })
+ self.glucoseStore.authorize(toShare: false, { _ in })
+ }
+
+ completion()
+ }
+ }
+}
diff --git a/Learn/Managers/DayCalculator.swift b/Learn/Managers/DayCalculator.swift
new file mode 100644
index 0000000000..f46ffc9747
--- /dev/null
+++ b/Learn/Managers/DayCalculator.swift
@@ -0,0 +1,64 @@
+//
+// DayCalculator.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import LoopCore
+import LoopKit
+
+class DayCalculator {
+ typealias Calculator = (_ dataManager: DataManager, _ day: DateInterval, _ results: Locked, _ completion: @escaping (_ error: Error?) -> Void) -> Void
+
+ let dataManager: DataManager
+ let dates: DateInterval
+ private var lockedResults: Locked
+
+ init(dataManager: DataManager, dates: DateInterval, initial: ResultType) {
+ self.dataManager = dataManager
+ self.dates = dates
+ self.lockedResults = Locked(initial)
+ }
+
+ func execute(calculator: @escaping Calculator, completion: @escaping (_ result: Result) -> Void) {
+ var anyError: Error?
+
+ let group = DispatchGroup()
+
+ var segmentStart = dates.start
+
+ Calendar.current.enumerateDates(startingAfter: dates.start, matching: DateComponents(hour: 0), matchingPolicy: .nextTime) { (date, exactMatch, stop) in
+ guard let date = date else {
+ stop = true
+ return
+ }
+
+ let interval = DateInterval(start: segmentStart, end: min(dates.end, date))
+
+ guard interval.duration > 0 else {
+ stop = true
+ return
+ }
+
+ group.enter()
+ calculator(dataManager, interval, lockedResults) { error in
+ if let error = error {
+ anyError = error
+ }
+
+ group.leave()
+ }
+ segmentStart = interval.end
+ }
+
+ group.notify(queue: .main) {
+ if let error = anyError {
+ completion(.failure(error))
+ } else {
+ completion(.success(self.lockedResults.value))
+ }
+ }
+ }
+}
diff --git a/Learn/Models/TimeComponents.swift b/Learn/Models/TimeComponents.swift
new file mode 100644
index 0000000000..cf93a09635
--- /dev/null
+++ b/Learn/Models/TimeComponents.swift
@@ -0,0 +1,73 @@
+//
+// TimeComponents.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+struct TimeComponents: Equatable, Hashable {
+ let hour: Int
+ let minute: Int
+
+ init(hour: Int, minute: Int) {
+ if hour >= 24, minute >= 60 {
+ assertionFailure("Invalid time components: \(hour):\(minute)")
+ }
+
+ self.hour = hour
+ self.minute = minute
+ }
+
+ init?(dateComponents: DateComponents) {
+ guard let hour = dateComponents.hour, let minute = dateComponents.minute else {
+ return nil
+ }
+
+ self.init(hour: hour, minute: minute)
+ }
+
+ init(timeIntervalSinceMidnight timeInterval: TimeInterval) {
+ self.init(hour: Int(timeInterval.hours), minute: Int(timeInterval.minutes) % 60)
+ }
+
+ var dateComponents: DateComponents {
+ return DateComponents(hour: hour, minute: minute, second: 0)
+ }
+
+ var timeIntervalSinceMidnight: TimeInterval {
+ return TimeInterval(hours: Double(hour)) + TimeInterval(minutes: Double(minute))
+ }
+
+ func floored(to timeInterval: TimeInterval) -> TimeComponents {
+ let floored = floor(timeIntervalSinceMidnight / timeInterval) * timeInterval
+ return TimeComponents(timeIntervalSinceMidnight: floored)
+ }
+
+ func bucket(withBucketSize bucketSize: TimeInterval) -> Range {
+ let lowerBound = floored(to: bucketSize)
+ return lowerBound..<(lowerBound + bucketSize)
+ }
+
+ private func adding(_ timeInterval: TimeInterval) -> TimeComponents {
+ return TimeComponents(timeIntervalSinceMidnight: timeIntervalSinceMidnight + timeInterval)
+ }
+}
+
+extension TimeComponents: Comparable {
+ static func < (lhs: TimeComponents, rhs: TimeComponents) -> Bool {
+ if lhs.hour == rhs.hour {
+ return lhs.minute < rhs.minute
+ } else {
+ return lhs.hour < rhs.hour
+ }
+ }
+}
+
+extension TimeComponents {
+ static func + (lhs: TimeComponents, rhs: TimeInterval) -> TimeComponents {
+ return lhs.adding(rhs)
+ }
+}
diff --git a/Learn/View Controllers/LessonConfigurationViewController.swift b/Learn/View Controllers/LessonConfigurationViewController.swift
new file mode 100644
index 0000000000..c6476f2119
--- /dev/null
+++ b/Learn/View Controllers/LessonConfigurationViewController.swift
@@ -0,0 +1,161 @@
+//
+// LessonConfigurationViewController.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+import LoopKitUI
+
+class LessonConfigurationViewController: UITableViewController {
+
+ var lesson: Lesson!
+
+ private enum State {
+ case editing
+ case executing
+ }
+
+ private var state = State.editing {
+ didSet {
+ guard state != oldValue else {
+ return
+ }
+
+ if let cell = tableView.cellForRow(at: IndexPath(row: 0, section: lesson.configurationSections.count)) as? TextButtonTableViewCell {
+ cell.isLoading = state == .executing
+ cell.isEnabled = state == .editing
+ }
+ }
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ title = lesson.title
+ tableView.estimatedRowHeight = 44
+ tableView.rowHeight = UITableView.automaticDimension
+
+ for section in lesson.configurationSections {
+ for cell in section.cells {
+ cell.registerCell(for: self.tableView)
+ }
+ }
+
+ tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className)
+ }
+
+ /// If a tap occurs in the table view, but not on any cells, dismiss any active edits
+ @IBAction private func dismissActiveEditing(gestureRecognizer: UITapGestureRecognizer) {
+ let tapPoint = gestureRecognizer.location(in: tableView)
+ guard tableView.indexPathForRow(at: tapPoint) == nil else {
+ return
+ }
+
+ tableView.endEditing(false)
+ tableView.beginUpdates()
+ hideDatePickerCells()
+ tableView.endUpdates()
+ }
+
+ // MARK: - Table view data source
+
+ override func numberOfSections(in tableView: UITableView) -> Int {
+ return lesson.configurationSections.count + 1
+ }
+
+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ if section == lesson.configurationSections.count {
+ return 1
+ } else {
+ return lesson.configurationSections[section].cells.count
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ if indexPath.section == lesson.configurationSections.count {
+ let cell = tableView.dequeueReusableCell(withIdentifier: TextButtonTableViewCell.className, for: indexPath) as! TextButtonTableViewCell
+ cell.textLabel?.text = NSLocalizedString("Continue", comment: "Title of the button to begin lesson execution")
+
+ switch state {
+ case .editing:
+ cell.isEnabled = true
+ cell.isLoading = false
+ case .executing:
+ cell.isEnabled = false
+ cell.isLoading = true
+ }
+
+ return cell
+ } else {
+ return lesson.configurationSections[indexPath.section].cells[indexPath.item].tableView(tableView, cellForRowAt: indexPath)
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+ if section < lesson.configurationSections.count {
+ return lesson.configurationSections[section].headerTitle
+ } else {
+ return nil
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
+ if section < lesson.configurationSections.count {
+ return lesson.configurationSections[section].footerTitle
+ } else {
+ return nil
+ }
+ }
+
+ // MARK: - UITableViewDelegate
+
+ override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
+ return state == .editing
+ }
+
+ override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
+ guard case .editing = state else {
+ return nil
+ }
+
+ tableView.endEditing(false)
+ tableView.beginUpdates()
+ hideDatePickerCells(excluding: indexPath)
+ return indexPath
+ }
+
+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ tableView.endUpdates()
+ tableView.deselectRow(at: indexPath, animated: true)
+
+ if indexPath.section == lesson.configurationSections.count {
+ state = .executing
+
+ lesson.execute { resultSections in
+ dispatchPrecondition(condition: .onQueue(.main))
+
+ self.performSegue(withIdentifier: LessonResultsViewController.className, sender: resultSections)
+
+ self.state = .editing
+ }
+ }
+ }
+
+ override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
+ super.prepare(for: segue, sender: sender)
+
+ if let results = sender as? [LessonSectionProviding], let destination = segue.destination as? LessonResultsViewController {
+ destination.lesson = lesson
+ destination.results = results
+ }
+ }
+}
+
+
+extension LessonConfigurationViewController: DatePickerTableViewCellDelegate {
+ func datePickerTableViewCellDidUpdateDate(_ cell: DatePickerTableViewCell) {
+
+ }
+}
diff --git a/Learn/View Controllers/LessonResultsViewController.swift b/Learn/View Controllers/LessonResultsViewController.swift
new file mode 100644
index 0000000000..e9c45b6b44
--- /dev/null
+++ b/Learn/View Controllers/LessonResultsViewController.swift
@@ -0,0 +1,58 @@
+//
+// LessonResultsViewController.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import LoopCore
+import UIKit
+
+
+class LessonResultsViewController: UITableViewController, IdentifiableClass {
+
+ var lesson: Lesson!
+
+ var results: [LessonSectionProviding] = [] {
+ didSet {
+ if isViewLoaded {
+ tableView.reloadData()
+ }
+ }
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ title = lesson.title
+
+ for section in results {
+ for cell in section.cells {
+ cell.registerCell(for: self.tableView)
+ }
+ }
+ }
+
+ // MARK: - Table view data source
+
+ override func numberOfSections(in tableView: UITableView) -> Int {
+ return results.count
+ }
+
+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return results[section].cells.count
+ }
+
+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ return results[indexPath.section].cells[indexPath.row].tableView(tableView, cellForRowAt: indexPath)
+ }
+
+ override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
+ return nil
+ }
+
+ override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
+ return false
+ }
+
+}
diff --git a/Learn/View Controllers/LessonsViewController.swift b/Learn/View Controllers/LessonsViewController.swift
new file mode 100644
index 0000000000..a5c3b4ef7a
--- /dev/null
+++ b/Learn/View Controllers/LessonsViewController.swift
@@ -0,0 +1,46 @@
+//
+// LessonsViewController.swift
+// Learn
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+
+class LessonsViewController: UITableViewController {
+
+ var lessons: [Lesson] = [] {
+ didSet {
+ if isViewLoaded {
+ tableView.reloadData()
+ }
+ }
+ }
+
+ // MARK: - Table view data source
+
+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return lessons.count
+ }
+
+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = tableView.dequeueReusableCell(withIdentifier: "Lesson", for: indexPath)
+ let lesson = lessons[indexPath.row]
+
+ cell.textLabel?.text = lesson.title
+ cell.detailTextLabel?.text = lesson.subtitle
+
+ return cell
+ }
+
+ override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
+ super.prepare(for: segue, sender: sender)
+
+ if let configVC = segue.destination as? LessonConfigurationViewController,
+ let cell = sender as? UITableViewCell,
+ let indexPath = tableView.indexPath(for: cell)
+ {
+ configVC.lesson = lessons[indexPath.row]
+ }
+ }
+}
diff --git a/Learn/da.lproj/Localizable.strings b/Learn/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..e165a1d655
--- /dev/null
+++ b/Learn/da.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Beregner procentdelen af glukosemålinger inden for et specificeret interval";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Fortsæt";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Modal Dag";
+
+/* Lesson result text for no data */
+"No data available" = "Ingen data tilgængelige";
+
+/* Section title for glucose range */
+"Range" = "Interval";
+
+/* Title of config entry */
+"Start Date" = "Start Dato";
+
+/* Lesson title */
+"Time in Range" = "Tme in Range";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualiserer de hyppigste blodsukker værdier fordelt på dagen";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Weeks";
+
diff --git a/Learn/da.lproj/Main.strings b/Learn/da.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/da.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/de.lproj/Localizable.strings b/Learn/de.lproj/Localizable.strings
new file mode 100644
index 0000000000..40e3ea7b99
--- /dev/null
+++ b/Learn/de.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Computes the percentage of glucose measurements within a specified range";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continue";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Modal Day";
+
+/* Lesson result text for no data */
+"No data available" = "No data available";
+
+/* Section title for glucose range */
+"Range" = "Range";
+
+/* Title of config entry */
+"Start Date" = "Start Date";
+
+/* Lesson title */
+"Time in Range" = "Time in Range";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualizes the most frequent glucose values by time of day";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Weeks";
+
diff --git a/Learn/de.lproj/Main.strings b/Learn/de.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/de.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/en.lproj/Localizable.strings b/Learn/en.lproj/Localizable.strings
new file mode 100644
index 0000000000..44fdc3083b
--- /dev/null
+++ b/Learn/en.lproj/Localizable.strings
@@ -0,0 +1,32 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Computes the percentage of glucose measurements within a specified range";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continue";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Modal Day";
+
+/* Lesson result text for no data */
+"No data available" = "No data available";
+
+/* Section title for glucose range */
+"Range" = "Range";
+
+/* Title of config entry */
+"Start Date" = "Start Date";
+
+/* Lesson title */
+"Time in Range" = "Time in Range";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualizes the most frequent glucose values by time of day";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Weeks";
diff --git a/Learn/en.lproj/Main.strings b/Learn/en.lproj/Main.strings
new file mode 100644
index 0000000000..6b8f04c045
--- /dev/null
+++ b/Learn/en.lproj/Main.strings
@@ -0,0 +1,3 @@
+
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
diff --git a/Learn/es.lproj/Localizable.strings b/Learn/es.lproj/Localizable.strings
new file mode 100644
index 0000000000..c4c2a22a88
--- /dev/null
+++ b/Learn/es.lproj/Localizable.strings
@@ -0,0 +1,32 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Calcula el porcentaje de mediciones de glucosa en un rango específico";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continuar";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Máximo";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Mínimo";
+
+/* Lesson title */
+"Modal Day" = "Día modal";
+
+/* Lesson result text for no data */
+"No data available" = "No hay datos disponibles";
+
+/* Section title for glucose range */
+"Range" = "Rango";
+
+/* Title of config entry */
+"Start Date" = "Fecha de inicio";
+
+/* Lesson title */
+"Time in Range" = "Tiempo en rango";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Muestra los valores de glucosa más frecuentes en un momento del día";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Semanas";
diff --git a/Learn/es.lproj/Main.strings b/Learn/es.lproj/Main.strings
new file mode 100644
index 0000000000..912b8c2cc8
--- /dev/null
+++ b/Learn/es.lproj/Main.strings
@@ -0,0 +1,3 @@
+
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Aprender";
diff --git a/Learn/fi.lproj/Localizable.strings b/Learn/fi.lproj/Localizable.strings
new file mode 100644
index 0000000000..4b9c0d0d94
--- /dev/null
+++ b/Learn/fi.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Laskee glukoosimittausten prosenttimäärän määritellyllä alueella";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Jatka";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maksimi";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimi";
+
+/* Lesson title */
+"Modal Day" = "Tyypillinen päivä";
+
+/* Lesson result text for no data */
+"No data available" = "Tietoja ei saatavilla";
+
+/* Section title for glucose range */
+"Range" = "Alue";
+
+/* Title of config entry */
+"Start Date" = "Aloitusaika";
+
+/* Lesson title */
+"Time in Range" = "Aika tavoitealueella";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Näyttää yleisimmät glukoosiarvot vuorokaudenajan mukaan";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Viikkoa";
+
diff --git a/Learn/fi.lproj/Main.strings b/Learn/fi.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/fi.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/fr.lproj/Localizable.strings b/Learn/fr.lproj/Localizable.strings
new file mode 100644
index 0000000000..bf6bad0dbb
--- /dev/null
+++ b/Learn/fr.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Ceci calcule le pourcentage des mesures de glycémie dans une plage spécifique";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continuer";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Journée type";
+
+/* Lesson result text for no data */
+"No data available" = "Données indisponibles";
+
+/* Section title for glucose range */
+"Range" = "Plage";
+
+/* Title of config entry */
+"Start Date" = "Date de commencement";
+
+/* Lesson title */
+"Time in Range" = "Temps passé dans la cible";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualisation des glycémies les plus fréquentes selon l’heure";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Semaines";
+
diff --git a/Learn/fr.lproj/Main.strings b/Learn/fr.lproj/Main.strings
new file mode 100644
index 0000000000..8cb49e7404
--- /dev/null
+++ b/Learn/fr.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Apprendre";
+
diff --git a/Learn/it.lproj/Localizable.strings b/Learn/it.lproj/Localizable.strings
new file mode 100644
index 0000000000..aa9e31ecde
--- /dev/null
+++ b/Learn/it.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Calcola la percentuale delle misurazioni di glucosio all’interno di un intervallo specifico";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continua";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Massimo";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimo";
+
+/* Lesson title */
+"Modal Day" = "Modalità giornaliera";
+
+/* Lesson result text for no data */
+"No data available" = "Nessun dato disponibile";
+
+/* Section title for glucose range */
+"Range" = "Intervallo";
+
+/* Title of config entry */
+"Start Date" = "Data di inizio";
+
+/* Lesson title */
+"Time in Range" = "Tempo nell’intervallo";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualizza i valori di glucosio più frequenti per ora del giorno";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Settimane";
+
diff --git a/Learn/it.lproj/Main.strings b/Learn/it.lproj/Main.strings
new file mode 100644
index 0000000000..ee4118be50
--- /dev/null
+++ b/Learn/it.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Impara";
+
diff --git a/Learn/ja.lproj/Localizable.strings b/Learn/ja.lproj/Localizable.strings
new file mode 100644
index 0000000000..b1fd2f7e6b
--- /dev/null
+++ b/Learn/ja.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "指定範囲内の測定値の割合を算出";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "次へ";
+
+/* Placeholder for upper range entry */
+"Maximum" = "最大";
+
+/* Placeholder for lower range entry */
+"Minimum" = "最小";
+
+/* Lesson title */
+"Modal Day" = "Modal Day";
+
+/* Lesson result text for no data */
+"No data available" = "データがありません";
+
+/* Section title for glucose range */
+"Range" = "範囲";
+
+/* Title of config entry */
+"Start Date" = "開始日";
+
+/* Lesson title */
+"Time in Range" = "タイムインレンジ";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "頻度の高い測定値を時間ごとに表示";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "週";
+
diff --git a/Learn/ja.lproj/Main.strings b/Learn/ja.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/ja.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/nb.lproj/Localizable.strings b/Learn/nb.lproj/Localizable.strings
new file mode 100644
index 0000000000..a8c11c17c1
--- /dev/null
+++ b/Learn/nb.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Computes the percentage of glucose measurements within a specified range";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Fortsett";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maksimum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Modal dag";
+
+/* Lesson result text for no data */
+"No data available" = "Ingen data tilgjengelig";
+
+/* Section title for glucose range */
+"Range" = "Målområde";
+
+/* Title of config entry */
+"Start Date" = "Startdato";
+
+/* Lesson title */
+"Time in Range" = "Tid i målområdet";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualiser de nyeste blodsukkerverdier etter tid på døgnet";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Uker";
+
diff --git a/Learn/nb.lproj/Main.strings b/Learn/nb.lproj/Main.strings
new file mode 100644
index 0000000000..d8d90ac21a
--- /dev/null
+++ b/Learn/nb.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Lær";
+
diff --git a/Learn/nl.lproj/Localizable.strings b/Learn/nl.lproj/Localizable.strings
new file mode 100644
index 0000000000..b436db44fe
--- /dev/null
+++ b/Learn/nl.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Berekent het percentage glucosemetingen in een specifiek bereik";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Ga verder";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Model dag";
+
+/* Lesson result text for no data */
+"No data available" = "Geen data aanwezig";
+
+/* Section title for glucose range */
+"Range" = "Bereik";
+
+/* Title of config entry */
+"Start Date" = "Start datum";
+
+/* Lesson title */
+"Time in Range" = "Tijd in bereik";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Geeft de meest voorkomende glucose waardes weer per moment van de dag";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Weken";
+
diff --git a/Learn/nl.lproj/Main.strings b/Learn/nl.lproj/Main.strings
new file mode 100644
index 0000000000..7929420678
--- /dev/null
+++ b/Learn/nl.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Leer";
+
diff --git a/Learn/pl.lproj/Localizable.strings b/Learn/pl.lproj/Localizable.strings
new file mode 100644
index 0000000000..257d2c7a5a
--- /dev/null
+++ b/Learn/pl.lproj/Localizable.strings
@@ -0,0 +1,32 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Oblicza odsetek pomiarów glukozy w określonym zakresie";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Kontynuuj";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maks.";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Min.";
+
+/* Lesson title */
+"Modal Day" = "Dzień modalny";
+
+/* Lesson result text for no data */
+"No data available" = "Brak dostępnych danych";
+
+/* Section title for glucose range */
+"Range" = "Zakres";
+
+/* Title of config entry */
+"Start Date" = "Data rozpoczęcia";
+
+/* Lesson title */
+"Time in Range" = "Czas w zakresie";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Wizualizuje najczęstsze wartości glukozy według pory dnia";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Tygodnie";
diff --git a/Learn/pl.lproj/Main.strings b/Learn/pl.lproj/Main.strings
new file mode 100644
index 0000000000..6b8f04c045
--- /dev/null
+++ b/Learn/pl.lproj/Main.strings
@@ -0,0 +1,3 @@
+
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
diff --git a/Learn/pt-BR.lproj/Localizable.strings b/Learn/pt-BR.lproj/Localizable.strings
new file mode 100644
index 0000000000..cad806a303
--- /dev/null
+++ b/Learn/pt-BR.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Calcula a porcentagem de medições de glicose dentro de um intervalo especificado";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continuar";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Máximo";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Mínimo";
+
+/* Lesson title */
+"Modal Day" = "Dia Modal";
+
+/* Lesson result text for no data */
+"No data available" = "Não há dados disponíveis";
+
+/* Section title for glucose range */
+"Range" = "Variação";
+
+/* Title of config entry */
+"Start Date" = "Data de Início";
+
+/* Lesson title */
+"Time in Range" = "Tempo na Meta";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visualiza os valores de glicose mais frequentes por hora do dia";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Semanas";
+
diff --git a/Learn/pt-BR.lproj/Main.strings b/Learn/pt-BR.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/pt-BR.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/ro.lproj/Localizable.strings b/Learn/ro.lproj/Localizable.strings
new file mode 100644
index 0000000000..8ee962ab12
--- /dev/null
+++ b/Learn/ro.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Calculează procentul măsurătorilor glicemice dintr-un interval specificat";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Continuă";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maxim";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minim";
+
+/* Lesson title */
+"Modal Day" = "Zi modală";
+
+/* Lesson result text for no data */
+"No data available" = "Date inexistente";
+
+/* Section title for glucose range */
+"Range" = "Interval";
+
+/* Title of config entry */
+"Start Date" = "Dată inițială";
+
+/* Lesson title */
+"Time in Range" = "Timp petrecut în interval";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Vizualizează cele mai frecvente valori glicemice în funcție de oră";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Săptămâni";
+
diff --git a/Learn/ro.lproj/Main.strings b/Learn/ro.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/ro.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/ru.lproj/Localizable.strings b/Learn/ru.lproj/Localizable.strings
new file mode 100644
index 0000000000..2de0b3d286
--- /dev/null
+++ b/Learn/ru.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Вычисляет процент замеров ГК в указанном диапазоне";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Продолжить";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Максимум";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Минимум";
+
+/* Lesson title */
+"Modal Day" = "Модальный день";
+
+/* Lesson result text for no data */
+"No data available" = "Нет данных";
+
+/* Section title for glucose range */
+"Range" = "Диапазон";
+
+/* Title of config entry */
+"Start Date" = "Дата начала";
+
+/* Lesson title */
+"Time in Range" = "Время в диапазоне";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Показывает самые частые значения ГК по времени суток";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Недель";
+
diff --git a/Learn/ru.lproj/Main.strings b/Learn/ru.lproj/Main.strings
new file mode 100644
index 0000000000..a6bda48fca
--- /dev/null
+++ b/Learn/ru.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Обучение";
+
diff --git a/Learn/sv.lproj/Localizable.strings b/Learn/sv.lproj/Localizable.strings
new file mode 100644
index 0000000000..d4f055f26c
--- /dev/null
+++ b/Learn/sv.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Beräknar procentandelen glukosmätningar inom ett specifikt målvärde";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Fortsätt";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Maximum";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Minimum";
+
+/* Lesson title */
+"Modal Day" = "Genomsnittlig dag";
+
+/* Lesson result text for no data */
+"No data available" = "Ingen data tillgänglig";
+
+/* Section title for glucose range */
+"Range" = "Målvärde";
+
+/* Title of config entry */
+"Start Date" = "Starttid";
+
+/* Lesson title */
+"Time in Range" = "Tid inom målvärde";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Visar de vanligaste glukosvärdena under olika tider på dagen";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Veckor";
+
diff --git a/Learn/sv.lproj/Main.strings b/Learn/sv.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/sv.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/vi.lproj/Localizable.strings b/Learn/vi.lproj/Localizable.strings
new file mode 100644
index 0000000000..e35055c296
--- /dev/null
+++ b/Learn/vi.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "Tính tỉ lệ phần trăm của glucose trong một phạm vi nhất định";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "Tiếp tục";
+
+/* Placeholder for upper range entry */
+"Maximum" = "Tối đa";
+
+/* Placeholder for lower range entry */
+"Minimum" = "Tối thiểu";
+
+/* Lesson title */
+"Modal Day" = "Ngày chuẩn";
+
+/* Lesson result text for no data */
+"No data available" = "Dữ liệu không tồn tại";
+
+/* Section title for glucose range */
+"Range" = "Phạm vi";
+
+/* Title of config entry */
+"Start Date" = "Ngày bắt đầu";
+
+/* Lesson title */
+"Time in Range" = "Thời gian trong phạm vi kiểm soát";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "Hình dung các giá trị glucose thường xuyên nhất theo thời gian trong ngày";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "Tuần";
+
diff --git a/Learn/vi.lproj/Main.strings b/Learn/vi.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/vi.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Learn/zh-Hans.lproj/Localizable.strings b/Learn/zh-Hans.lproj/Localizable.strings
new file mode 100644
index 0000000000..9db52c7bac
--- /dev/null
+++ b/Learn/zh-Hans.lproj/Localizable.strings
@@ -0,0 +1,33 @@
+/* Lesson subtitle */
+"Computes the percentage of glucose measurements within a specified range" = "计算在指定范围内的血糖测量值的百分比";
+
+/* Title of the button to begin lesson execution */
+"Continue" = "继续";
+
+/* Placeholder for upper range entry */
+"Maximum" = "最大";
+
+/* Placeholder for lower range entry */
+"Minimum" = "最小";
+
+/* Lesson title */
+"Modal Day" = "Modal Day";
+
+/* Lesson result text for no data */
+"No data available" = "无数据";
+
+/* Section title for glucose range */
+"Range" = "范围";
+
+/* Title of config entry */
+"Start Date" = "开始日期";
+
+/* Lesson title */
+"Time in Range" = "在目标范围的时间";
+
+/* Lesson subtitle */
+"Visualizes the most frequent glucose values by time of day" = "全天血糖数据";
+
+/* Unit string for a count of calendar weeks */
+"Weeks" = "周";
+
diff --git a/Learn/zh-Hans.lproj/Main.strings b/Learn/zh-Hans.lproj/Main.strings
new file mode 100644
index 0000000000..50fa41e306
--- /dev/null
+++ b/Learn/zh-Hans.lproj/Main.strings
@@ -0,0 +1,3 @@
+/* Class = "UINavigationItem"; title = "Learn"; ObjectID = "8hF-Ij-B7m"; */
+"8hF-Ij-B7m.title" = "Learn";
+
diff --git a/Loop Status Extension/Base.lproj/InfoPlist.strings b/Loop Status Extension/Base.lproj/InfoPlist.strings
index 9250064a26..f8e9a2b43f 100644
--- a/Loop Status Extension/Base.lproj/InfoPlist.strings
+++ b/Loop Status Extension/Base.lproj/InfoPlist.strings
@@ -1,6 +1,3 @@
-/* (No Comment) */
-"CFBundleDisplayName" = "Loop";
-
/* (No Comment) */
"CFBundleName" = "$(PRODUCT_NAME)";
diff --git a/Loop Status Extension/Base.lproj/Localizable.strings b/Loop Status Extension/Base.lproj/Localizable.strings
index 4c9097d64c..d21551845d 100644
--- a/Loop Status Extension/Base.lproj/Localizable.strings
+++ b/Loop Status Extension/Base.lproj/Localizable.strings
@@ -1,50 +1,5 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "A model based on the published absorption of Fiasp insulin.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "An adjustment to the adult model based on empirical effects in children.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Eventually %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Eventually %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ U";
-
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Rapid-Acting – Adults";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Rapid-Acting – Children";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "The legacy model used by Loop, allowing customization of action duration.";
-
-/* The short unit display string for international units of insulin */
-"U" = "U";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
diff --git a/Loop Status Extension/Base.lproj/MainInterface.storyboard b/Loop Status Extension/Base.lproj/MainInterface.storyboard
index 5b5eb74e56..3c66dec3b8 100644
--- a/Loop Status Extension/Base.lproj/MainInterface.storyboard
+++ b/Loop Status Extension/Base.lproj/MainInterface.storyboard
@@ -1,11 +1,9 @@
-
-
-
-
+
+
-
+
@@ -65,7 +63,6 @@
-
diff --git a/Loop Status Extension/Info.plist b/Loop Status Extension/Info.plist
index c5b92dfe7e..ebfb453f39 100644
--- a/Loop Status Extension/Info.plist
+++ b/Loop Status Extension/Info.plist
@@ -2,10 +2,10 @@
+ AppGroupIdentifier
+ $(APP_GROUP_IDENTIFIER)
CFBundleDevelopmentRegion
en
- MainAppBundleIdentifier
- $(MAIN_APP_BUNDLE_IDENTIFIER)
CFBundleDisplayName
Loop
CFBundleExecutable
@@ -19,11 +19,11 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 1.9.5
+ $(LOOP_MARKETING_VERSION)-spike
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
- AppGroupIdentifier
- $(APP_GROUP_IDENTIFIER)
+ MainAppBundleIdentifier
+ $(MAIN_APP_BUNDLE_IDENTIFIER)
NSExtension
NSExtensionMainStoryboard
diff --git a/Loop Status Extension/StateColorPalette.swift b/Loop Status Extension/StateColorPalette.swift
index c4e67ba9c4..e6f18b436a 100644
--- a/Loop Status Extension/StateColorPalette.swift
+++ b/Loop Status Extension/StateColorPalette.swift
@@ -6,6 +6,7 @@
//
import LoopUI
+import LoopKitUI
extension StateColorPalette {
static let loopStatus = StateColorPalette(unknown: .unknownColor, normal: .freshColor, warning: .agingColor, error: .staleColor)
diff --git a/Loop Status Extension/StatusChartsManager.swift b/Loop Status Extension/StatusChartsManager.swift
new file mode 100644
index 0000000000..9473ef17c1
--- /dev/null
+++ b/Loop Status Extension/StatusChartsManager.swift
@@ -0,0 +1,19 @@
+//
+// StatusChartsManager.swift
+// Loop Status Extension
+//
+// Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import LoopUI
+import SwiftCharts
+import UIKit
+
+class StatusChartsManager: ChartsManager {
+ let predictedGlucose = PredictedGlucoseChart()
+
+ init(colors: ChartColorPalette, settings: ChartSettings, traitCollection: UITraitCollection) {
+ super.init(colors: colors, settings: settings, charts: [predictedGlucose], traitCollection: traitCollection)
+ }
+}
diff --git a/Loop Status Extension/StatusViewController.swift b/Loop Status Extension/StatusViewController.swift
index 68d1856898..f009729b5d 100644
--- a/Loop Status Extension/StatusViewController.swift
+++ b/Loop Status Extension/StatusViewController.swift
@@ -9,6 +9,8 @@
import CoreData
import HealthKit
import LoopKit
+import LoopKitUI
+import LoopCore
import LoopUI
import NotificationCenter
import UIKit
@@ -22,8 +24,6 @@ class StatusViewController: UIViewController, NCWidgetProviding {
hudView.glucoseHUD.stateColors = .cgmStatus
hudView.glucoseHUD.tintColor = .glucoseTintColor
hudView.basalRateHUD.tintColor = .doseTintColor
- hudView.reservoirVolumeHUD.stateColors = .pumpStatus
- hudView.batteryHUD.stateColors = .pumpStatus
}
}
@IBOutlet weak var subtitleLabel: UILabel!
@@ -48,20 +48,18 @@ class StatusViewController: UIViewController, NCWidgetProviding {
settings.labelsToAxisSpacingX = 6
settings.clipInnerFrame = false
return settings
- }()
+ }(),
+ traitCollection: traitCollection
)
- charts.glucoseDisplayRange = (
- min: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 100),
- max: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 175)
- )
+ charts.predictedGlucose.glucoseDisplayRange = HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 100)...HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 175)
return charts
}()
var statusExtensionContext: StatusExtensionContext?
- lazy var defaults = UserDefaults(suiteName: Bundle.main.appGroupSuiteName)
+ lazy var defaults = UserDefaults.appGroup
private var observers: [Any] = []
@@ -83,30 +81,43 @@ class StatusViewController: UIViewController, NCWidgetProviding {
basalProfile: defaults?.basalRateSchedule,
insulinSensitivitySchedule: defaults?.insulinSensitivitySchedule
)
+
+ private var pluginManager: PluginManager = {
+ let containingAppFrameworksURL = Bundle.main.privateFrameworksURL?.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().appendingPathComponent("Frameworks")
+ return PluginManager(pluginsURL: containingAppFrameworksURL)
+ }()
override func viewDidLoad() {
super.viewDidLoad()
subtitleLabel.isHidden = true
- subtitleLabel.textColor = .subtitleLabelColor
+ if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
+ subtitleLabel.textColor = .secondaryLabel
+ insulinLabel.textColor = .secondaryLabel
+ } else {
+ subtitleLabel.textColor = .subtitleLabelColor
+ insulinLabel.textColor = .subtitleLabelColor
+ }
+
insulinLabel.isHidden = true
- insulinLabel.textColor = .subtitleLabelColor
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openLoopApp(_:)))
view.addGestureRecognizer(tapGestureRecognizer)
self.charts.prerender()
glucoseChartContentView.chartGenerator = { [weak self] (frame) in
- return self?.charts.glucoseChartWithFrame(frame)?.view
+ return self?.charts.chart(atIndex: 0, frame: frame)?.view
}
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
switch extensionContext?.widgetActiveDisplayMode ?? .compact {
- case .compact:
- glucoseChartContentView.isHidden = true
case .expanded:
glucoseChartContentView.isHidden = false
+ case .compact:
+ fallthrough
+ @unknown default:
+ glucoseChartContentView.isHidden = true
}
observers = [
@@ -124,10 +135,12 @@ class StatusViewController: UIViewController, NCWidgetProviding {
let compactHeight = hudView.systemLayoutSizeFitting(maxSize).height + subtitleLabel.systemLayoutSizeFitting(maxSize).height
switch activeDisplayMode {
- case .compact:
- preferredContentSize = CGSize(width: maxSize.width, height: compactHeight)
case .expanded:
preferredContentSize = CGSize(width: maxSize.width, height: compactHeight + 100)
+ case .compact:
+ fallthrough
+ @unknown default:
+ preferredContentSize = CGSize(width: maxSize.width, height: compactHeight)
}
}
@@ -139,6 +152,10 @@ class StatusViewController: UIViewController, NCWidgetProviding {
self.glucoseChartContentView.isHidden = self.extensionContext?.widgetActiveDisplayMode != .expanded
})
}
+
+ override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+ charts.traitCollection = traitCollection
+ }
@objc private func openLoopApp(_: Any) {
if let url = Bundle.main.mainAppUrl {
@@ -159,7 +176,6 @@ class StatusViewController: UIViewController, NCWidgetProviding {
let group = DispatchGroup()
var activeInsulin: Double?
- var lastReservoirValue: ReservoirValue?
var glucose: [StoredGlucoseSample] = []
group.enter()
@@ -173,17 +189,6 @@ class StatusViewController: UIViewController, NCWidgetProviding {
group.leave()
}
- group.enter()
- doseStore.getReservoirValues(since: .distantPast, limit: 1) { (result) in
- switch result {
- case .success(let values):
- lastReservoirValue = values.first
- case .failure:
- lastReservoirValue = nil
- }
- group.leave()
- }
-
charts.startDate = Calendar.current.nextDate(after: Date(timeIntervalSinceNow: .minutes(-5)), matching: DateComponents(minute: 0), matchingPolicy: .strict, direction: .backward) ?? Date()
// Showing the whole history plus full prediction in the glucose plot
@@ -201,13 +206,20 @@ class StatusViewController: UIViewController, NCWidgetProviding {
return
}
- if let batteryPercentage = context.batteryPercentage {
- self.hudView.batteryHUD.batteryLevel = Double(batteryPercentage)
+ let hudViews: [BaseHUDView]
+
+ if let hudViewsContext = context.pumpManagerHUDViewsContext,
+ let contextHUDViews = PumpManagerHUDViewsFromRawValue(hudViewsContext.pumpManagerHUDViewsRawValue, pluginManager: self.pluginManager)
+ {
+ hudViews = contextHUDViews
+ } else {
+ hudViews = [ReservoirVolumeHUDView.instantiate(), BatteryLevelHUDView.instantiate()]
}
- if let reservoir = lastReservoirValue, let capacity = context.reservoirCapacity {
- self.hudView.reservoirVolumeHUD.reservoirLevel = min(1, max(0, Double(reservoir.unitVolume / capacity)))
- self.hudView.reservoirVolumeHUD.setReservoirVolume(volume: reservoir.unitVolume, at: reservoir.startDate)
+ self.hudView.removePumpManagerProvidedViews()
+ for view in hudViews {
+ view.stateColors = .pumpStatus
+ self.hudView.addHUDView(view)
}
if let netBasal = context.netBasal {
@@ -253,58 +265,42 @@ class StatusViewController: UIViewController, NCWidgetProviding {
)
}
- let glucoseFormatter = NumberFormatter.glucoseFormatter(for: unit)
-
- let dateFormatter: DateFormatter = {
- let dateFormatter = DateFormatter()
- dateFormatter.dateStyle = .none
- dateFormatter.timeStyle = .short
+ let glucoseFormatter = QuantityFormatter()
+ glucoseFormatter.setPreferredNumberFormatter(for: unit)
- return dateFormatter
- }()
-
- self.charts.glucoseUnit = unit
- self.charts.glucosePoints = glucose.map {
- ChartPoint(
- x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter),
- y: ChartAxisValueDoubleUnit($0.quantity.doubleValue(for: unit), unitString: unit.localizedShortUnitString, formatter: glucoseFormatter)
- )
- }
+ self.charts.predictedGlucose.glucoseUnit = unit
+ self.charts.predictedGlucose.setGlucoseValues(glucose)
if let predictedGlucose = context.predictedGlucose?.samples {
- self.charts.predictedGlucosePoints = predictedGlucose.map {
- ChartPoint(
- x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter),
- y: ChartAxisValueDoubleUnit($0.quantity.doubleValue(for: unit), unitString: unit.localizedShortUnitString, formatter: glucoseFormatter)
- )
- }
+ self.charts.predictedGlucose.setPredictedGlucoseValues(predictedGlucose)
if let eventualGlucose = predictedGlucose.last {
- if let eventualGlucoseNumberString = glucoseFormatter.string(from: eventualGlucose.quantity.doubleValue(for: unit)) {
+ if let eventualGlucoseNumberString = glucoseFormatter.string(from: eventualGlucose.quantity, for: unit) {
self.subtitleLabel.text = String(
format: NSLocalizedString(
- "Eventually %1$@ %2$@",
- comment: "The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description)"
+ "Eventually %1$@",
+ comment: "The subtitle format describing eventual glucose. (1: localized glucose value description)"
),
- eventualGlucoseNumberString,
- unit.localizedShortUnitString
+ eventualGlucoseNumberString
)
self.subtitleLabel.isHidden = false
}
}
}
- self.charts.targetGlucoseSchedule = defaults.loopSettings?.glucoseTargetRangeSchedule
-
+ self.charts.predictedGlucose.targetGlucoseSchedule = defaults.loopSettings?.glucoseTargetRangeSchedule
+ self.charts.invalidateChart(atIndex: 0)
self.charts.prerender()
self.glucoseChartContentView.reloadChart()
}
switch extensionContext?.widgetActiveDisplayMode ?? .compact {
- case .compact:
- glucoseChartContentView.isHidden = true
case .expanded:
glucoseChartContentView.isHidden = false
+ case .compact:
+ fallthrough
+ @unknown default:
+ glucoseChartContentView.isHidden = true
}
// Right now we always act as if there's new data.
diff --git a/Loop Status Extension/da.lproj/Localizable.strings b/Loop Status Extension/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..fdea45f6b6
--- /dev/null
+++ b/Loop Status Extension/da.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Til sidst %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ E";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
diff --git a/Loop Status Extension/da.lproj/MainInterface.strings b/Loop Status Extension/da.lproj/MainInterface.strings
new file mode 100644
index 0000000000..00e818d7d5
--- /dev/null
+++ b/Loop Status Extension/da.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Til sidst 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 E";
+
diff --git a/Loop Status Extension/de.lproj/InfoPlist.strings b/Loop Status Extension/de.lproj/InfoPlist.strings
deleted file mode 100644
index 9cd95a0151..0000000000
--- a/Loop Status Extension/de.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* Bundle display name */
-"CFBundleDisplayName" = "Loop";
-
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/de.lproj/Localizable.strings b/Loop Status Extension/de.lproj/Localizable.strings
index e18f2cf64d..d82776beac 100644
--- a/Loop Status Extension/de.lproj/Localizable.strings
+++ b/Loop Status Extension/de.lproj/Localizable.strings
@@ -1,51 +1,9 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "Ein Modell basierend auf der veröffentlichten Absorption von Fiasp-Insulin.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "Ein Modell auf der Grundlage der veröffentlichten Absorption von Humalog, Novolog und Apidra Insulin bei Erwachsenen.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Eine Anpassung an das Erwachsenenmodell basierend auf empirischen Effekten bei Kindern.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Schließlich %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Schließlich %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ E";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Schnell handelnd - Erwachsene";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Schnell handelnd - Kinder";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Das Legacy-Modell, das von Loop verwendet wird und die Anpassung der Aktionsdauer ermöglicht.";
-
/* The short unit display string for international units of insulin */
-"U" = "E";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
+"U" = "IE";
diff --git a/Loop Status Extension/de.lproj/MainInterface.strings b/Loop Status Extension/de.lproj/MainInterface.strings
index f72bf72bc2..df78268386 100644
--- a/Loop Status Extension/de.lproj/MainInterface.strings
+++ b/Loop Status Extension/de.lproj/MainInterface.strings
@@ -2,5 +2,5 @@
"9iF-xY-Bh4.text" = "Schließlich 92 mg/dL";
/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
-"UPi-dG-yYD.text" = "IOB 1.0 E";
+"UPi-dG-yYD.text" = "IOB 1.0 IE";
diff --git a/Loop Status Extension/en.lproj/Localizable.strings b/Loop Status Extension/en.lproj/Localizable.strings
new file mode 100644
index 0000000000..d21551845d
--- /dev/null
+++ b/Loop Status Extension/en.lproj/Localizable.strings
@@ -0,0 +1,5 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Eventually %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ U";
diff --git a/Loop Status Extension/en.lproj/MainInterface.strings b/Loop Status Extension/en.lproj/MainInterface.strings
new file mode 100644
index 0000000000..0f89462fa3
--- /dev/null
+++ b/Loop Status Extension/en.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Eventually 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 U";
diff --git a/Loop Status Extension/es.lproj/InfoPlist.strings b/Loop Status Extension/es.lproj/InfoPlist.strings
deleted file mode 100644
index 9cd95a0151..0000000000
--- a/Loop Status Extension/es.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* Bundle display name */
-"CFBundleDisplayName" = "Loop";
-
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/es.lproj/Localizable.strings b/Loop Status Extension/es.lproj/Localizable.strings
index 689064591d..44c2b74318 100644
--- a/Loop Status Extension/es.lproj/Localizable.strings
+++ b/Loop Status Extension/es.lproj/Localizable.strings
@@ -1,51 +1,5 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "Un modelo basado en la publicación de la absorción de insulina Fiasp.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "Un modelo basado en la publicación de la absorción de insulina Humalog, Novolog y Apidra en adultos.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Un ajuste al modelo adulto basado en los efectos empíricos en niños.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Eventualmente %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Eventualmente %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ U";
-
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Acción Rápida — Adultos";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Acción Rápida — Niños";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "El model utilizado por ediciones iniciales de Loop, permite ajustar duración de acción.";
-
-/* The short unit display string for international units of insulin */
-"U" = "U";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop Status Extension/fi.lproj/Localizable.strings b/Loop Status Extension/fi.lproj/Localizable.strings
new file mode 100644
index 0000000000..1dbfbd5cf3
--- /dev/null
+++ b/Loop Status Extension/fi.lproj/Localizable.strings
@@ -0,0 +1,6 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Ennuste %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ U";
+
diff --git a/Loop Status Extension/fi.lproj/MainInterface.strings b/Loop Status Extension/fi.lproj/MainInterface.strings
new file mode 100644
index 0000000000..9bb1d27d3d
--- /dev/null
+++ b/Loop Status Extension/fi.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Ennuste 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 U";
+
diff --git a/Loop Status Extension/fr.lproj/InfoPlist.strings b/Loop Status Extension/fr.lproj/InfoPlist.strings
deleted file mode 100644
index 9cd95a0151..0000000000
--- a/Loop Status Extension/fr.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* Bundle display name */
-"CFBundleDisplayName" = "Loop";
-
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/fr.lproj/Localizable.strings b/Loop Status Extension/fr.lproj/Localizable.strings
index 1baaaf0570..bf675dcc9a 100644
--- a/Loop Status Extension/fr.lproj/Localizable.strings
+++ b/Loop Status Extension/fr.lproj/Localizable.strings
@@ -1,51 +1,6 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "Un modèle basé sur l’absorption publiée de l’insuline FIASP.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "Un modèle basé sur l’absorption publiée de l’Hunalog, Novolog, et Apidra chez l’adulte.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Un ajustement au modèle adulte basé sur des effets empiriques chez les enfants.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Éventuellement %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Prévision : %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ U";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Action rapide - Adulte";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Action rapide - Enfant";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Le modèle hérité utilisé par Loop, permettant de personnaliser la durée de l’action.";
-
-/* The short unit display string for international units of insulin */
-"U" = "U";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop Status Extension/it.lproj/InfoPlist.strings b/Loop Status Extension/it.lproj/InfoPlist.strings
deleted file mode 100644
index 9cd95a0151..0000000000
--- a/Loop Status Extension/it.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* Bundle display name */
-"CFBundleDisplayName" = "Loop";
-
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/it.lproj/Localizable.strings b/Loop Status Extension/it.lproj/Localizable.strings
index 732d79d715..8d36fd4bed 100644
--- a/Loop Status Extension/it.lproj/Localizable.strings
+++ b/Loop Status Extension/it.lproj/Localizable.strings
@@ -1,51 +1,6 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "Modello basato sull'assorbimento dell'insulina Fiasp.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "Modello basato sull'assorbimento negli adulti dell'insulina Humalog, Novolog ed Apidra.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Un adattamento al modello adulto basato su effetti empirici nei bambini.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Probabile Glic. %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Probabile Glic. %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ U";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Insulina ultrarapida – Adulti";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Insulina ultrarapida – Bambini";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "ll modello legacy utilizzato da Loop, che consente la personalizzazione della durata dell'azione.";
-
-/* The short unit display string for international units of insulin */
-"U" = "U";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop Status Extension/ja.lproj/Localizable.strings b/Loop Status Extension/ja.lproj/Localizable.strings
new file mode 100644
index 0000000000..0f9b31a8ea
--- /dev/null
+++ b/Loop Status Extension/ja.lproj/Localizable.strings
@@ -0,0 +1,6 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "予想 %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ U";
+
diff --git a/Loop Status Extension/ja.lproj/MainInterface.strings b/Loop Status Extension/ja.lproj/MainInterface.strings
new file mode 100644
index 0000000000..3e7844c42b
--- /dev/null
+++ b/Loop Status Extension/ja.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "予想 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 U";
+
diff --git a/Loop Status Extension/nb.lproj/InfoPlist.strings b/Loop Status Extension/nb.lproj/InfoPlist.strings
deleted file mode 100644
index 9250064a26..0000000000
--- a/Loop Status Extension/nb.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* (No Comment) */
-"CFBundleDisplayName" = "Loop";
-
-/* (No Comment) */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/nb.lproj/Localizable.strings b/Loop Status Extension/nb.lproj/Localizable.strings
index 766f3ff5d7..af0e1fc932 100644
--- a/Loop Status Extension/nb.lproj/Localizable.strings
+++ b/Loop Status Extension/nb.lproj/Localizable.strings
@@ -1,51 +1,9 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "En modell basert på publiserte data for absorpsjon av Fiasp insulin.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "En modell basert på publiserte data for absorpsjon av Humalog, Novolog og Apidra insulin hos voksne.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "En justering til modellen tilpasset voksne, basert på empiriske effekter hos barn.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Omsider %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Omsider %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ E";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Hurtigvirkende – voksen";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Hurtigvirkende – barn";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Den gamle modellen brukt av Loop, tillater endring av varighet for tiltak.";
-
/* The short unit display string for international units of insulin */
"U" = "E";
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop Status Extension/nl.lproj/InfoPlist.strings b/Loop Status Extension/nl.lproj/InfoPlist.strings
deleted file mode 100644
index 9250064a26..0000000000
--- a/Loop Status Extension/nl.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* (No Comment) */
-"CFBundleDisplayName" = "Loop";
-
-/* (No Comment) */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/nl.lproj/Localizable.strings b/Loop Status Extension/nl.lproj/Localizable.strings
index d245c795f3..20a25693db 100644
--- a/Loop Status Extension/nl.lproj/Localizable.strings
+++ b/Loop Status Extension/nl.lproj/Localizable.strings
@@ -1,51 +1,9 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "Een model gebaseerd op de gepubliceerde opname van Fiasp insuline.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "Een model gebaseerd op de gepubliceerde opname van Humalog, Novorapid, en Apidra insuline bij volwassenen.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Een aanpassing aan het volwassen model gebaseerd om praktijk effecten bij kinderen.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Uiteindelijk %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "gr";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Uiteindelijk %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ E";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Snelwerkende - volwassenen";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Snelwerkende - kinderen";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Het oude model dat door Loop wordt gebruikt, waardoor de actieduur kan worden aangepast.";
-
/* The short unit display string for international units of insulin */
"U" = "E";
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop Status Extension/pl.lproj/InfoPlist.strings b/Loop Status Extension/pl.lproj/InfoPlist.strings
deleted file mode 100644
index 9250064a26..0000000000
--- a/Loop Status Extension/pl.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* (No Comment) */
-"CFBundleDisplayName" = "Loop";
-
-/* (No Comment) */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/pl.lproj/Localizable.strings b/Loop Status Extension/pl.lproj/Localizable.strings
index faab632afe..6522679f83 100644
--- a/Loop Status Extension/pl.lproj/Localizable.strings
+++ b/Loop Status Extension/pl.lproj/Localizable.strings
@@ -1,50 +1,5 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "Model oparty na opublikowanym czasie absorpcji insuliny Fiasp.";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "Model oparty na opublikowanym czasie absorpcji insulin Humalog, Novolog/Novorapid i Apidra u dorosłych.";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Model absorpcji insuliny u dzieci oparty na empirycznym dostosowaniu czasu absorpcji u dorosłych.";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "Docelowo %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "g";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Docelowo %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ J";
-
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "mg/dL";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "mmol/L";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Szybko działająca – dorośli";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Szybko działająca – dzieci";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Model umożliwiający dostosowanie czasu działania insuliny.";
-
-/* The short unit display string for international units of insulin */
-"U" = "J";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
diff --git a/Loop Status Extension/pt-BR.lproj/Localizable.strings b/Loop Status Extension/pt-BR.lproj/Localizable.strings
new file mode 100644
index 0000000000..7d3b493829
--- /dev/null
+++ b/Loop Status Extension/pt-BR.lproj/Localizable.strings
@@ -0,0 +1,6 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Eventualmente %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ U";
+
diff --git a/Loop Status Extension/pt-BR.lproj/MainInterface.strings b/Loop Status Extension/pt-BR.lproj/MainInterface.strings
new file mode 100644
index 0000000000..8d4fcfa1e0
--- /dev/null
+++ b/Loop Status Extension/pt-BR.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Eventualmente 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 U";
+
diff --git a/Loop Status Extension/ro.lproj/Localizable.strings b/Loop Status Extension/ro.lproj/Localizable.strings
new file mode 100644
index 0000000000..177cf6e0c5
--- /dev/null
+++ b/Loop Status Extension/ro.lproj/Localizable.strings
@@ -0,0 +1,6 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Eventually %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ U";
+
diff --git a/Loop Status Extension/ro.lproj/MainInterface.strings b/Loop Status Extension/ro.lproj/MainInterface.strings
new file mode 100644
index 0000000000..3eb5d43c9e
--- /dev/null
+++ b/Loop Status Extension/ro.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Eventually 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 U";
+
diff --git a/Loop Status Extension/ru.lproj/InfoPlist.strings b/Loop Status Extension/ru.lproj/InfoPlist.strings
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/Loop Status Extension/ru.lproj/Localizable.strings b/Loop Status Extension/ru.lproj/Localizable.strings
index eaad302889..1049e0663e 100644
--- a/Loop Status Extension/ru.lproj/Localizable.strings
+++ b/Loop Status Extension/ru.lproj/Localizable.strings
@@ -1,51 +1,6 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "модель, основанная на опубликованных данных усвоения FIASP инсулина";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "модель, основанная на опубликованных данных усвоения Humalog, Novolog и Apidra у взрослых";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "Поправка к модели для взрослых основанная на эмпирических данных для детей";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "В конечном итоге %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "г";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "В конечном итоге %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ ед";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "мг/дл";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "ммоль/л";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "Боыстродействующий - взрослые";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "Быстродействующий - дети";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Модель, используемая для цикла/контура, позволяет настройку длительности действия";
-
-/* The short unit display string for international units of insulin */
-"U" = "ед";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop Status Extension/sv.lproj/Localizable.strings b/Loop Status Extension/sv.lproj/Localizable.strings
new file mode 100644
index 0000000000..630360f562
--- /dev/null
+++ b/Loop Status Extension/sv.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Eventuellt %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ E";
+
+/* The short unit display string for international units of insulin */
+"U" = "E";
+
diff --git a/Loop Status Extension/sv.lproj/MainInterface.strings b/Loop Status Extension/sv.lproj/MainInterface.strings
new file mode 100644
index 0000000000..89e8f748e8
--- /dev/null
+++ b/Loop Status Extension/sv.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Eventuellt 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 E";
+
diff --git a/Loop Status Extension/vi.lproj/Localizable.strings b/Loop Status Extension/vi.lproj/Localizable.strings
new file mode 100644
index 0000000000..471ea04c90
--- /dev/null
+++ b/Loop Status Extension/vi.lproj/Localizable.strings
@@ -0,0 +1,6 @@
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "Kết quả là %1$@";
+
+/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
+"IOB %1$@ U" = "IOB %1$@ U";
+
diff --git a/Loop Status Extension/vi.lproj/MainInterface.strings b/Loop Status Extension/vi.lproj/MainInterface.strings
new file mode 100644
index 0000000000..66ec8fb074
--- /dev/null
+++ b/Loop Status Extension/vi.lproj/MainInterface.strings
@@ -0,0 +1,6 @@
+/* Class = "UILabel"; text = "Eventually 92 mg/dL"; ObjectID = "9iF-xY-Bh4"; */
+"9iF-xY-Bh4.text" = "Kết quả là 92 mg/dL";
+
+/* Class = "UILabel"; text = "IOB 1.0 U"; ObjectID = "UPi-dG-yYD"; */
+"UPi-dG-yYD.text" = "IOB 1.0 U";
+
diff --git a/Loop Status Extension/zh-Hans.lproj/InfoPlist.strings b/Loop Status Extension/zh-Hans.lproj/InfoPlist.strings
deleted file mode 100644
index 9cd95a0151..0000000000
--- a/Loop Status Extension/zh-Hans.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,6 +0,0 @@
-/* Bundle display name */
-"CFBundleDisplayName" = "Loop";
-
-/* Bundle name */
-"CFBundleName" = "$(PRODUCT_NAME)";
-
diff --git a/Loop Status Extension/zh-Hans.lproj/Localizable.strings b/Loop Status Extension/zh-Hans.lproj/Localizable.strings
index 79541f11d5..b1d62cfb8c 100644
--- a/Loop Status Extension/zh-Hans.lproj/Localizable.strings
+++ b/Loop Status Extension/zh-Hans.lproj/Localizable.strings
@@ -1,51 +1,6 @@
-/* The format string for the app name and version number. (1: bundle name)(2: bundle version) */
-"%1$@ v%2$@" = "%1$@ v%2$@";
-
-/* Subtitle of Fiasp preset */
-"A model based on the published absorption of Fiasp insulin." = "基于公布的Fiasp胰岛素吸收的模型";
-
-/* Subtitle of Rapid-Acting – Adult preset */
-"A model based on the published absorption of Humalog, Novolog, and Apidra insulin in adults." = "基于已公布的Humalog,Novolog和Apidra胰岛素在成人中吸收的模型。";
-
-/* Subtitle of Rapid-Acting – Children preset */
-"An adjustment to the adult model based on empirical effects in children." = "在成人胰岛素模型基础上专为儿童修改的胰岛素代谢模型";
-
-/* The short unit display string for decibles */
-"dB" = "dB";
-
-/* The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description) */
-"Eventually %1$@ %2$@" = "最终 %1$@ %2$@";
-
-/* Title of insulin model preset */
-"Fiasp" = "Fiasp";
-
-/* The short unit display string for grams */
-"g" = "克";
+/* The subtitle format describing eventual glucose. (1: localized glucose value description) */
+"Eventually %1$@" = "最终 %1$@";
/* The subtitle format describing units of active insulin. (1: localized insulin value description) */
"IOB %1$@ U" = "IOB %1$@ 单位";
-/* The short unit display string for milligrams of glucose per decilter */
-"mg/dL" = "毫克/分升";
-
-/* The short unit display string for millimoles of glucose per liter */
-"mmol/L" = "毫摩尔/升";
-
-/* Format string for combining localized numeric value and unit. (1: numeric value)(2: unit) */
-"QUANTITY_VALUE_AND_UNIT" = "%1$@ %2$@";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Adults" = "速效胰岛素 - 成人模型";
-
-/* Title of insulin model preset */
-"Rapid-Acting – Children" = "速效胰岛素 - 儿童模型";
-
-/* Subtitle description of Walsh insulin model setting */
-"The legacy model used by Loop, allowing customization of action duration." = "Loop使用的默认模型参数,您可以自行修改胰岛素代谢时间。";
-
-/* The short unit display string for international units of insulin */
-"U" = "单位";
-
-/* Title of insulin model setting */
-"Walsh" = "Walsh";
-
diff --git a/Loop.xcconfig b/Loop.xcconfig
index ab57d0d2a7..f1333b1564 100644
--- a/Loop.xcconfig
+++ b/Loop.xcconfig
@@ -9,3 +9,12 @@
// This is automatically disambiguated by development team, but you may choose to change this to
// support running multiple apps simultaneously.
MAIN_APP_BUNDLE_IDENTIFIER = com.${DEVELOPMENT_TEAM}.loopkit
+MAIN_APP_DISPLAY_NAME = Loop
+
+LOOP_MARKETING_VERSION = 2.0
+
+APPICON_NAME = AppIcon
+
+// Optional workspace configuration overrides
+#include? "../LoopConfigOverride.xcconfig"
+
diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj
index d69a59fb6a..496ff57e7a 100644
--- a/Loop.xcodeproj/project.pbxproj
+++ b/Loop.xcodeproj/project.pbxproj
@@ -22,29 +22,28 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 2E497FEA22554CE3008E41F6 /* SpikeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E497FE822554CE3008E41F6 /* SpikeClient.framework */; };
+ 2E497FEB22554CE3008E41F6 /* SpikeClientUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E497FE922554CE3008E41F6 /* SpikeClientUI.framework */; };
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 */; };
- 430B298A2041F54A00BA9F93 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29892041F54A00BA9F93 /* NSUserDefaults.swift */; };
- 430B298B2041F55700BA9F93 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29892041F54A00BA9F93 /* NSUserDefaults.swift */; };
- 430B298E2041F56500BA9F93 /* LoopSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298C2041F56500BA9F93 /* LoopSettings.swift */; };
- 430B298F2041F56500BA9F93 /* GlucoseThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */; };
- 430B29902041F57000BA9F93 /* GlucoseThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */; };
- 430B29912041F57200BA9F93 /* LoopSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298C2041F56500BA9F93 /* LoopSettings.swift */; };
430B29932041F5B300BA9F93 /* UserDefaults+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29922041F5B200BA9F93 /* UserDefaults+Loop.swift */; };
430B29952041F5CB00BA9F93 /* LoopSettings+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29942041F5CB00BA9F93 /* LoopSettings+Loop.swift */; };
430D85891F44037000AF2D4F /* HUDViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430D85881F44037000AF2D4F /* HUDViewTableViewCell.swift */; };
- 430DA58E1D4AEC230097D1CA /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
4311FB9B1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4311FB9A1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift */; };
4315D2871CA5CC3B00589052 /* CarbEntryEditTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4315D2861CA5CC3B00589052 /* CarbEntryEditTableViewController.swift */; };
4315D28A1CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4315D2891CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift */; };
431A8C401EC6E8AB00823B9C /* CircleMaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431A8C3F1EC6E8AB00823B9C /* CircleMaskView.swift */; };
- 431E73481FF95A900069B5F7 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E73471FF95A900069B5F7 /* PersistenceController.swift */; };
+ 431EA87021EB29120076EC1A /* ExponentialInsulinModelPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */; };
+ 431EA87121EB29120076EC1A /* ExponentialInsulinModelPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */; };
+ 431EA87221EB29150076EC1A /* InsulinModelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6281F37B01300C320C7 /* InsulinModelSettings.swift */; };
+ 431EA87321EB29160076EC1A /* InsulinModelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6281F37B01300C320C7 /* InsulinModelSettings.swift */; };
+ 431EA87421EB291A0076EC1A /* WalshInsulinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6261F37AE5600C320C7 /* WalshInsulinModel.swift */; };
+ 431EA87521EB291B0076EC1A /* WalshInsulinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6261F37AE5600C320C7 /* WalshInsulinModel.swift */; };
4326BA641F3A44D9007CCAD4 /* ChartLineModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4326BA631F3A44D9007CCAD4 /* ChartLineModel.swift */; };
4328E01A1CFBE1DA00E199AA /* ActionHUDController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0151CFBE1DA00E199AA /* ActionHUDController.swift */; };
4328E01B1CFBE1DA00E199AA /* BolusInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0161CFBE1DA00E199AA /* BolusInterfaceController.swift */; };
4328E01E1CFBE25F00E199AA /* AddCarbsInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E01D1CFBE25F00E199AA /* AddCarbsInterfaceController.swift */; };
- 4328E0261CFBE2C500E199AA /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0201CFBE2C500E199AA /* IdentifiableClass.swift */; };
4328E0281CFBE2C500E199AA /* CLKComplicationTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0221CFBE2C500E199AA /* CLKComplicationTemplate.swift */; };
4328E02A1CFBE2C500E199AA /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0241CFBE2C500E199AA /* UIColor.swift */; };
4328E02B1CFBE2C500E199AA /* WKAlertAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0251CFBE2C500E199AA /* WKAlertAction.swift */; };
@@ -56,7 +55,6 @@
433EA4C41D9F71C800CD78FB /* CommandResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433EA4C31D9F71C800CD78FB /* CommandResponseViewController.swift */; };
4341F4EB1EDB92AC001C936B /* LogglyService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4341F4EA1EDB92AC001C936B /* LogglyService.swift */; };
43441A9C1EDB34810087958C /* StatusExtensionContext+LoopKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43441A9B1EDB34810087958C /* StatusExtensionContext+LoopKit.swift */; };
- 4344628020A7A37400C4BE6F /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344627F20A7A37400C4BE6F /* HealthKit.framework */; };
4344628220A7A37F00C4BE6F /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344628120A7A37E00C4BE6F /* CoreBluetooth.framework */; };
4344628520A7A3BE00C4BE6F /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344628320A7A3BE00C4BE6F /* LoopKit.framework */; };
4344628620A7A3BE00C4BE6F /* CGMBLEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344628420A7A3BE00C4BE6F /* CGMBLEKit.framework */; };
@@ -64,58 +62,52 @@
4344628F20A7ADD500C4BE6F /* UserDefaults+CGM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4344628D20A7ADD100C4BE6F /* UserDefaults+CGM.swift */; };
4344629220A7C19800C4BE6F /* ButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4344629120A7C19800C4BE6F /* ButtonGroup.swift */; };
4344629820A8B2D700C4BE6F /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5EE209D84BE00D17AA8 /* OSLog.swift */; };
+ 4345E3F421F036FC009E00E5 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D848AF1E7DCBE100DADCBC /* Result.swift */; };
+ 4345E3F521F036FC009E00E5 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D848AF1E7DCBE100DADCBC /* Result.swift */; };
+ 4345E3F821F03D2A009E00E5 /* DatesAndNumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345E3F721F03D2A009E00E5 /* DatesAndNumberCell.swift */; };
+ 4345E3FA21F0473B009E00E5 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345E3F921F0473B009E00E5 /* TextCell.swift */; };
+ 4345E3FB21F04911009E00E5 /* UIColor+HIG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0C31E4659E700FF19A9 /* UIColor+HIG.swift */; };
+ 4345E3FC21F04911009E00E5 /* UIColor+HIG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0C31E4659E700FF19A9 /* UIColor+HIG.swift */; };
+ 4345E3FE21F04A50009E00E5 /* DateIntervalFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345E3FD21F04A50009E00E5 /* DateIntervalFormatter.swift */; };
+ 4345E3FF21F051C6009E00E5 /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; };
+ 4345E40021F051DD009E00E5 /* LoopCore.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 4345E40121F67300009E00E5 /* CarbEntryUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DE92581C5479E4001FFDE1 /* CarbEntryUserInfo.swift */; };
+ 4345E40221F67300009E00E5 /* CarbEntryUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DE92581C5479E4001FFDE1 /* CarbEntryUserInfo.swift */; };
+ 4345E40421F68AD9009E00E5 /* TextRowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345E40321F68AD9009E00E5 /* TextRowController.swift */; };
+ 4345E40621F68E18009E00E5 /* CarbEntryListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345E40521F68E18009E00E5 /* CarbEntryListController.swift */; };
4346D1E71C77F5FE00ABAFE3 /* ChartTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4346D1E61C77F5FE00ABAFE3 /* ChartTableViewCell.swift */; };
- 434B2886206628B3000EE07B /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E73471FF95A900069B5F7 /* PersistenceController.swift */; };
- 434B2887206B4F07000EE07B /* WalshInsulinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6261F37AE5600C320C7 /* WalshInsulinModel.swift */; };
- 434B2888206B4F0A000EE07B /* InsulinModelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6281F37B01300C320C7 /* InsulinModelSettings.swift */; };
- 434B2889206B4F0C000EE07B /* ExponentialInsulinModelPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */; };
- 434F54571D287FDB002A9274 /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434F54561D287FDB002A9274 /* NibLoadable.swift */; };
+ 434A9F9923124B210047C077 /* BolusConfirmationScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434A9F9823124B210047C077 /* BolusConfirmationScene.swift */; };
434FB6461D68F1CD007B9C70 /* Amplitude.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 434FB6451D68F1CD007B9C70 /* Amplitude.framework */; };
- 434FF1EA1CF26C29000DB779 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */; };
434FF1EE1CF27EEF000DB779 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1ED1CF27EEF000DB779 /* UITableViewCell.swift */; };
- 43523EDB1CC35083001850F1 /* RileyLinkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43523EDA1CC35083001850F1 /* RileyLinkKit.framework */; };
- 435400311C9F744E00D5819C /* BolusSuggestionUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435400301C9F744E00D5819C /* BolusSuggestionUserInfo.swift */; };
- 435400321C9F745500D5819C /* BolusSuggestionUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435400301C9F744E00D5819C /* BolusSuggestionUserInfo.swift */; };
+ 43511CE221FD80E400566C63 /* RetrospectiveCorrection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43511CDF21FD80E400566C63 /* RetrospectiveCorrection.swift */; };
+ 43511CE321FD80E400566C63 /* StandardRetrospectiveCorrection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43511CE021FD80E400566C63 /* StandardRetrospectiveCorrection.swift */; };
+ 43511CEE220FC61700566C63 /* HUDRowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43511CED220FC61700566C63 /* HUDRowController.swift */; };
+ 43517915230A07100072ECC0 /* NumberFormatter+WatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43517914230A07100072ECC0 /* NumberFormatter+WatchApp.swift */; };
+ 43517917230A0E1A0072ECC0 /* WKInterfaceLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43517916230A0E1A0072ECC0 /* WKInterfaceLabel.swift */; };
435400341C9F878D00D5819C /* SetBolusUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435400331C9F878D00D5819C /* SetBolusUserInfo.swift */; };
435400351C9F878D00D5819C /* SetBolusUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435400331C9F878D00D5819C /* SetBolusUserInfo.swift */; };
435CB6231F37967800C320C7 /* InsulinModelSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6221F37967800C320C7 /* InsulinModelSettingsViewController.swift */; };
- 435CB6251F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */; };
- 435CB6271F37AE5600C320C7 /* WalshInsulinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6261F37AE5600C320C7 /* WalshInsulinModel.swift */; };
- 435CB6291F37B01300C320C7 /* InsulinModelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6281F37B01300C320C7 /* InsulinModelSettings.swift */; };
436961911F19D11E00447E89 /* ChartPointsContextFillLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4369618F1F19C86400447E89 /* ChartPointsContextFillLayer.swift */; };
436A0DA51D236A2A00104B24 /* LoopError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0DA41D236A2A00104B24 /* LoopError.swift */; };
436D9BF81F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json in Resources */ = {isa = PBXBuildFile; fileRef = 436D9BF71F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json */; };
- 43709AEA20DF3F8200F941B3 /* MinimedKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43709AE920DF3F8200F941B3 /* MinimedKitUI.framework */; };
4372E484213A63FB0068E043 /* ChartHUDController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFEDFBE20E5CF22000BFC58 /* ChartHUDController.swift */; };
4372E487213C86240068E043 /* SampleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E486213C86240068E043 /* SampleValue.swift */; };
4372E488213C862B0068E043 /* SampleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E486213C86240068E043 /* SampleValue.swift */; };
4372E48B213CB5F00068E043 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E48A213CB5F00068E043 /* Double.swift */; };
4372E48C213CB6750068E043 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E48A213CB5F00068E043 /* Double.swift */; };
- 4372E48D213CF8A70068E043 /* GlucoseThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */; };
- 4372E48E213CF8AD0068E043 /* LoopSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298C2041F56500BA9F93 /* LoopSettings.swift */; };
4372E490213CFCE70068E043 /* LoopSettingsUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E48F213CFCE70068E043 /* LoopSettingsUserInfo.swift */; };
4372E491213D05F90068E043 /* LoopSettingsUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E48F213CFCE70068E043 /* LoopSettingsUserInfo.swift */; };
4372E492213D956C0068E043 /* GlucoseRangeSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C513181E864C4E001547C7 /* GlucoseRangeSchedule.swift */; };
4372E496213DCDD30068E043 /* GlucoseChartValueHashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4372E495213DCDD30068E043 /* GlucoseChartValueHashable.swift */; };
- 4372E497213F79F90068E043 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29892041F54A00BA9F93 /* NSUserDefaults.swift */; };
- 4372E498213F7A550068E043 /* InsulinModelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6281F37B01300C320C7 /* InsulinModelSettings.swift */; };
- 4372E499213F7A6D0068E043 /* ExponentialInsulinModelPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */; };
- 4372E49A213F7A830068E043 /* WalshInsulinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6261F37AE5600C320C7 /* WalshInsulinModel.swift */; };
- 4372E49B213F7B340068E043 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
4374B5EF209D84BF00D17AA8 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5EE209D84BE00D17AA8 /* OSLog.swift */; };
4374B5F0209D857E00D17AA8 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5EE209D84BE00D17AA8 /* OSLog.swift */; };
- 4374B5F2209D897600D17AA8 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5F1209D897600D17AA8 /* Locked.swift */; };
- 4374B5F4209D89A900D17AA8 /* TextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5F3209D89A900D17AA8 /* TextFieldTableViewCell.swift */; };
43776F901B8022E90074EA36 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43776F8F1B8022E90074EA36 /* AppDelegate.swift */; };
43776F971B8022E90074EA36 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43776F951B8022E90074EA36 /* Main.storyboard */; };
- 43776F991B8022E90074EA36 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43776F981B8022E90074EA36 /* Assets.xcassets */; };
43785E932120A01B0057DED1 /* NewCarbEntryIntent+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43785E922120A01B0057DED1 /* NewCarbEntryIntent+Loop.swift */; };
43785E972120E4500057DED1 /* INRelevantShortcutStore+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43785E952120E4010057DED1 /* INRelevantShortcutStore+Loop.swift */; };
43785E982120E7060057DED1 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 43785E9B2120E7060057DED1 /* Intents.intentdefinition */; };
4379CFF021112CF700AADC79 /* ShareClientUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4379CFEF21112CF700AADC79 /* ShareClientUI.framework */; };
- 437AFEE42035252A008C4892 /* RileyLinkBLEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 434AB0B11CBB4C3300422F4A /* RileyLinkBLEKit.framework */; };
437AFEE520352591008C4892 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F70C1DD1DE8DCA7006380B7 /* NotificationCenter.framework */; };
- 437AFEE7203688CF008C4892 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 437AFEE6203688CF008C4892 /* LoopKitUI.framework */; };
437AFEE8203689FE008C4892 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; };
437CEEE41CDE5C0A003C8C80 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEE31CDE5C0A003C8C80 /* UIImage.swift */; };
437D9BA31D7BC977007245E8 /* PredictionTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437D9BA21D7BC977007245E8 /* PredictionTableViewController.swift */; };
@@ -128,6 +120,8 @@
438D42F91D7C88BC003244B0 /* PredictionInputEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438D42F81D7C88BC003244B0 /* PredictionInputEffect.swift */; };
438D42FB1D7D11A4003244B0 /* PredictionInputEffectTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438D42FA1D7D11A4003244B0 /* PredictionInputEffectTableViewCell.swift */; };
43947D731F529FAA00A07D31 /* GlucoseRangeSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C513181E864C4E001547C7 /* GlucoseRangeSchedule.swift */; };
+ 4396BD50225159C0005AA4D3 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9002C21EB225D00AF44BF /* HealthKit.framework */; };
+ 439706E622D2E84900C81566 /* PredictionSettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439706E522D2E84900C81566 /* PredictionSettingTableViewCell.swift */; };
439897371CD2F80600223065 /* AnalyticsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897361CD2F80600223065 /* AnalyticsManager.swift */; };
439A7942211F631C0041B75F /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439A7941211F631C0041B75F /* RootNavigationController.swift */; };
439A7944211FE22F0041B75F /* NSUserActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439A7943211FE22F0041B75F /* NSUserActivity.swift */; };
@@ -150,34 +144,87 @@
43BFF0B71E45C20C00FF19A9 /* NumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0B31E45C1BE00FF19A9 /* NumberFormatter.swift */; };
43BFF0BC1E45C80600FF19A9 /* UIColor+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0BB1E45C80600FF19A9 /* UIColor+Loop.swift */; };
43BFF0BF1E45C8EA00FF19A9 /* UIColor+Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0BE1E45C8EA00FF19A9 /* UIColor+Widget.swift */; };
- 43BFF0C51E465A2D00FF19A9 /* UIColor+HIG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0C31E4659E700FF19A9 /* UIColor+HIG.swift */; };
43BFF0C61E465A4400FF19A9 /* UIColor+HIG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0C31E4659E700FF19A9 /* UIColor+HIG.swift */; };
- 43BFF0C71E465A4F00FF19A9 /* UIColor+HIG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0C31E4659E700FF19A9 /* UIColor+HIG.swift */; };
- 43BFF0C91E465B0A00FF19A9 /* StateColorPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0C81E465B0A00FF19A9 /* StateColorPalette.swift */; };
- 43BFF0CB1E466C0900FF19A9 /* StateColorPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0CA1E466C0900FF19A9 /* StateColorPalette.swift */; };
43BFF0CD1E466C8400FF19A9 /* StateColorPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0CC1E466C8400FF19A9 /* StateColorPalette.swift */; };
+ 43C05CA821EB2B26006FB252 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E73471FF95A900069B5F7 /* PersistenceController.swift */; };
+ 43C05CA921EB2B26006FB252 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E73471FF95A900069B5F7 /* PersistenceController.swift */; };
+ 43C05CAA21EB2B49006FB252 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
+ 43C05CAB21EB2B4A006FB252 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
+ 43C05CAC21EB2B8B006FB252 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
+ 43C05CAD21EB2BBF006FB252 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29892041F54A00BA9F93 /* NSUserDefaults.swift */; };
+ 43C05CAE21EB2BBF006FB252 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29892041F54A00BA9F93 /* NSUserDefaults.swift */; };
+ 43C05CAF21EB2C24006FB252 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
+ 43C05CB121EBBDB9006FB252 /* TimeInRangeLesson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CB021EBBDB9006FB252 /* TimeInRangeLesson.swift */; };
+ 43C05CB221EBD88A006FB252 /* LoopCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9002A21EB209400AF44BF /* LoopCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 43C05CB521EBE274006FB252 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CB421EBE274006FB252 /* Date.swift */; };
+ 43C05CB621EBE321006FB252 /* NSTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897341CD2F7DE00223065 /* NSTimeInterval.swift */; };
+ 43C05CB821EBEA54006FB252 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CB721EBEA54006FB252 /* HKUnit.swift */; };
+ 43C05CB921EBEA54006FB252 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CB721EBEA54006FB252 /* HKUnit.swift */; };
+ 43C05CBA21EBEAD8006FB252 /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; };
+ 43C05CBD21EBF77D006FB252 /* LessonsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CBC21EBF77D006FB252 /* LessonsViewController.swift */; };
+ 43C05CC021EBFFA4006FB252 /* Lesson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CBF21EBFFA4006FB252 /* Lesson.swift */; };
+ 43C05CC221EC06E4006FB252 /* LessonConfigurationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CC121EC06E4006FB252 /* LessonConfigurationViewController.swift */; };
+ 43C05CC521EC29E3006FB252 /* TextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5F3209D89A900D17AA8 /* TextFieldTableViewCell.swift */; };
+ 43C05CC621EC29E7006FB252 /* TextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5F3209D89A900D17AA8 /* TextFieldTableViewCell.swift */; };
+ 43C05CC721EC2ABC006FB252 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */; };
+ 43C05CC821EC2ABC006FB252 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */; };
+ 43C05CCA21EC382B006FB252 /* NumberEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C05CC921EC382B006FB252 /* NumberEntry.swift */; };
43C0944A1CACCC73001F6403 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C094491CACCC73001F6403 /* NotificationManager.swift */; };
43C246A81D89990F0031F8D1 /* Crypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43C246A71D89990F0031F8D1 /* Crypto.framework */; };
43C2FAE11EB656A500364AFF /* GlucoseEffectVelocity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2FAE01EB656A500364AFF /* GlucoseEffectVelocity.swift */; };
43C3B6EC20B650A80026CAFA /* SettingsImageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C3B6EB20B650A80026CAFA /* SettingsImageTableViewCell.swift */; };
- 43C3B6ED20B884500026CAFA /* GlucoseThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */; };
43C513191E864C4E001547C7 /* GlucoseRangeSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C513181E864C4E001547C7 /* GlucoseRangeSchedule.swift */; };
- 43CA93371CB98079000026B5 /* MinimedKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43CA93361CB98079000026B5 /* MinimedKit.framework */; };
+ 43C5F257222C7B7200905D10 /* TimeComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C5F256222C7B7200905D10 /* TimeComponents.swift */; };
+ 43C5F258222C7BD400905D10 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9FFA421EA9A0C00AF44BF /* AppDelegate.swift */; };
+ 43C5F25A222C921B00905D10 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C5F259222C921B00905D10 /* OSLog.swift */; };
+ 43C728F5222266F000C62969 /* ModalDayLesson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C728F4222266F000C62969 /* ModalDayLesson.swift */; };
+ 43C728F72222700000C62969 /* DateIntervalEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C728F62222700000C62969 /* DateIntervalEntry.swift */; };
+ 43C728F9222A448700C62969 /* DayCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C728F8222A448700C62969 /* DayCalculator.swift */; };
43CB2B2B1D924D450079823D /* WCSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CB2B2A1D924D450079823D /* WCSession.swift */; };
43CE7CDE1CA8B63E003CC1B0 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CDD1CA8B63E003CC1B0 /* Data.swift */; };
43CEE6E61E56AFD400CB9116 /* NightscoutUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CEE6E51E56AFD400CB9116 /* NightscoutUploader.swift */; };
43D2E8231F00425400AE5CBF /* BolusViewController+LoopDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D2E8221F00425400AE5CBF /* BolusViewController+LoopDataManager.swift */; };
43D381621EBD9759007F8C8F /* HeaderValuesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D381611EBD9759007F8C8F /* HeaderValuesTableViewCell.swift */; };
- 43D848B01E7DCBE100DADCBC /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D848AF1E7DCBE100DADCBC /* Result.swift */; };
+ 43D9000B21EB0BE000AF44BF /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; };
+ 43D9001E21EB209400AF44BF /* LoopCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 43D9FFD121EAE05D00AF44BF /* LoopCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 43D9002021EB209400AF44BF /* NSTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897341CD2F7DE00223065 /* NSTimeInterval.swift */; };
+ 43D9002121EB209400AF44BF /* GlucoseThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */; };
+ 43D9002221EB209400AF44BF /* LoopSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298C2041F56500BA9F93 /* LoopSettings.swift */; };
+ 43D9002D21EB225D00AF44BF /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9002C21EB225D00AF44BF /* HealthKit.framework */; };
+ 43D9002E21EB226F00AF44BF /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4344628320A7A3BE00C4BE6F /* LoopKit.framework */; };
+ 43D9002F21EB234400AF44BF /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9002A21EB209400AF44BF /* LoopCore.framework */; };
+ 43D9003321EB258C00AF44BF /* InsulinModelSettings+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9003221EB258C00AF44BF /* InsulinModelSettings+Loop.swift */; };
+ 43D9F81821EC51CC000578CD /* DateEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9F81721EC51CC000578CD /* DateEntry.swift */; };
+ 43D9F81A21EC593C000578CD /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9F81921EC593C000578CD /* UITableViewCell.swift */; };
+ 43D9F81E21EF0609000578CD /* NumberRangeEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9F81D21EF0609000578CD /* NumberRangeEntry.swift */; };
+ 43D9F82021EF0906000578CD /* NSNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9F81F21EF0906000578CD /* NSNumber.swift */; };
+ 43D9F82221EF0A7A000578CD /* QuantityRangeEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9F82121EF0A7A000578CD /* QuantityRangeEntry.swift */; };
+ 43D9F82421EFF1AB000578CD /* LessonResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9F82321EFF1AB000578CD /* LessonResultsViewController.swift */; };
+ 43D9FFAA21EA9A0C00AF44BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43D9FFA821EA9A0C00AF44BF /* Main.storyboard */; };
+ 43D9FFAC21EA9A0F00AF44BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43D9FFAB21EA9A0F00AF44BF /* Assets.xcassets */; };
+ 43D9FFAF21EA9A0F00AF44BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43D9FFAD21EA9A0F00AF44BF /* LaunchScreen.storyboard */; };
+ 43D9FFB421EA9AD800AF44BF /* LoopUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F75288B1DFE1DC600C322D6 /* LoopUI.framework */; };
+ 43D9FFB621EA9B2F00AF44BF /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F5C2C81B929C09003EB13D /* HealthKit.framework */; };
+ 43D9FFBB21EA9CC900AF44BF /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; };
+ 43D9FFBC21EA9CCD00AF44BF /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 437AFEE6203688CF008C4892 /* LoopKitUI.framework */; };
+ 43D9FFBD21EA9CD700AF44BF /* SwiftCharts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */; };
+ 43D9FFC021EAB22E00AF44BF /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D9FFBF21EAB22E00AF44BF /* DataManager.swift */; };
+ 43D9FFD321EAE05D00AF44BF /* LoopCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 43D9FFD121EAE05D00AF44BF /* LoopCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 43D9FFD621EAE05D00AF44BF /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; };
+ 43D9FFD721EAE05D00AF44BF /* LoopCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 43D9FFDE21EAE3AE00AF44BF /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; };
+ 43D9FFE021EAE3E500AF44BF /* LoopUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F75288B1DFE1DC600C322D6 /* LoopUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 43D9FFE121EAE3E500AF44BF /* LoopCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 43D9FFF521EAF27200AF44BF /* LoopSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298C2041F56500BA9F93 /* LoopSettings.swift */; };
+ 43D9FFF821EAF2EF00AF44BF /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; };
+ 43D9FFF921EAF34800AF44BF /* GlucoseThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */; };
+ 43D9FFFA21EAF35900AF44BF /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F5C2C81B929C09003EB13D /* HealthKit.framework */; };
+ 43D9FFFB21EAF3D300AF44BF /* NSTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897341CD2F7DE00223065 /* NSTimeInterval.swift */; };
43DAD00020A2736F000F8529 /* PersistedPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DACFFF20A2736F000F8529 /* PersistedPumpEvent.swift */; };
43DBF04C1C93B8D700B3C386 /* BolusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DBF04B1C93B8D700B3C386 /* BolusViewController.swift */; };
43DBF0531C93EC8200B3C386 /* DeviceDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DBF0521C93EC8200B3C386 /* DeviceDataManager.swift */; };
43DBF0591C93F73800B3C386 /* CarbEntryTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DBF0581C93F73800B3C386 /* CarbEntryTableViewController.swift */; };
- 43DE92591C5479E4001FFDE1 /* CarbEntryUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DE92581C5479E4001FFDE1 /* CarbEntryUserInfo.swift */; };
- 43DE925A1C5479E4001FFDE1 /* CarbEntryUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DE92581C5479E4001FFDE1 /* CarbEntryUserInfo.swift */; };
- 43DE92611C555C26001FFDE1 /* AbsorptionTimeType+CarbKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DE92601C555C26001FFDE1 /* AbsorptionTimeType+CarbKit.swift */; };
43DFB62320D4CAE7008A7BAE /* PumpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C3B6F620BBCAA30026CAFA /* PumpManager.swift */; };
- 43E0F0A51E46D1670064F7CE /* LevelHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E0F0A41E46D1670064F7CE /* LevelHUDView.swift */; };
43E2D8C81D208D5B004DA55F /* KeychainManager+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E2D8C71D208D5B004DA55F /* KeychainManager+Loop.swift */; };
43E2D8D41D20BF42004DA55F /* DoseMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E2D8D31D20BF42004DA55F /* DoseMathTests.swift */; };
43E2D8DB1D20C03B004DA55F /* NSTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897341CD2F7DE00223065 /* NSTimeInterval.swift */; };
@@ -194,7 +241,6 @@
43E2D8F51D20C0DB004DA55F /* recommend_temp_basal_start_low_end_high.json in Resources */ = {isa = PBXBuildFile; fileRef = 43E2D8EA1D20C0DB004DA55F /* recommend_temp_basal_start_low_end_high.json */; };
43E2D8F61D20C0DB004DA55F /* recommend_temp_basal_start_low_end_in_range.json in Resources */ = {isa = PBXBuildFile; fileRef = 43E2D8EB1D20C0DB004DA55F /* recommend_temp_basal_start_low_end_in_range.json */; };
43E2D9151D20C5A2004DA55F /* KeychainManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E2D8C91D20B9E7004DA55F /* KeychainManagerTests.swift */; };
- 43E2D9171D2226BD004DA55F /* LoopKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
43E2D9191D222759004DA55F /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; };
43E3449F1B9D68E900C85C07 /* StatusTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E3449E1B9D68E900C85C07 /* StatusTableViewController.swift */; };
43E93FB51E4675E800EAB8DB /* NumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0B31E45C1BE00FF19A9 /* NumberFormatter.swift */; };
@@ -208,16 +254,29 @@
43F64DD91D9C92C900D24DC6 /* TitleSubtitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F64DD81D9C92C900D24DC6 /* TitleSubtitleTableViewCell.swift */; };
43F78D261C8FC000002152D1 /* DoseMath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F78D251C8FC000002152D1 /* DoseMath.swift */; };
43F78D4F1C914197002152D1 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; };
+ 43F89CA322BDFBBD006BB54E /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F89CA222BDFBBC006BB54E /* UIActivityIndicatorView.swift */; };
43FCBBC21E51710B00343C1B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43776F9A1B8022E90074EA36 /* LaunchScreen.storyboard */; };
+ 43FCEEA9221A615B0013DD30 /* StatusChartsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEA8221A615B0013DD30 /* StatusChartsManager.swift */; };
+ 43FCEEAB221A61B40013DD30 /* IOBChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEAA221A61B40013DD30 /* IOBChart.swift */; };
+ 43FCEEAD221A66780013DD30 /* DateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEAC221A66780013DD30 /* DateFormatter.swift */; };
+ 43FCEEAF221A67A70013DD30 /* NumberFormatter+Charts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEAE221A67A70013DD30 /* NumberFormatter+Charts.swift */; };
+ 43FCEEB1221A863E0013DD30 /* StatusChartsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEB0221A863E0013DD30 /* StatusChartsManager.swift */; };
+ 43FCEEB3221BC3B60013DD30 /* DoseChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEB2221BC3B60013DD30 /* DoseChart.swift */; };
+ 43FCEEB5221BCA020013DD30 /* COBChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEB4221BCA020013DD30 /* COBChart.swift */; };
+ 43FCEEB7221BCD160013DD30 /* InsulinModelChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEB6221BCD160013DD30 /* InsulinModelChart.swift */; };
+ 43FCEEB9221BCF790013DD30 /* GlucoseChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEB8221BCF790013DD30 /* GlucoseChart.swift */; };
+ 43FCEEBB22211C860013DD30 /* CarbEffectChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEBA22211C860013DD30 /* CarbEffectChart.swift */; };
+ 43FCEEBD22212DD50013DD30 /* PredictedGlucoseChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FCEEBC22212DD50013DD30 /* PredictedGlucoseChart.swift */; };
+ 43FCEEBE22220CE70013DD30 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 437AFEE6203688CF008C4892 /* LoopKitUI.framework */; };
+ 43FCEEBF22220CF30013DD30 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 437AFEE6203688CF008C4892 /* LoopKitUI.framework */; };
+ 43FCEEC022220D1F0013DD30 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; };
4D3B40041D4A9E1A00BC6334 /* G4ShareSpy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */; };
4F08DE8F1E7BB871006741EA /* CollectionType+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F08DE8E1E7BB871006741EA /* CollectionType+Loop.swift */; };
4F08DE9B1E7BC4ED006741EA /* SwiftCharts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */; };
- 4F08DE9D1E81D0E9006741EA /* StatusChartsManager+LoopKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430C1ABC1E5568A80067F1AE /* StatusChartsManager+LoopKit.swift */; };
4F11D3C020DCBEEC006E072C /* GlucoseBackfillRequestUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F11D3BF20DCBEEC006E072C /* GlucoseBackfillRequestUserInfo.swift */; };
4F11D3C220DD80B3006E072C /* WatchHistoricalGlucose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F11D3C120DD80B3006E072C /* WatchHistoricalGlucose.swift */; };
4F11D3C320DD84DB006E072C /* GlucoseBackfillRequestUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F11D3BF20DCBEEC006E072C /* GlucoseBackfillRequestUserInfo.swift */; };
4F11D3C420DD881A006E072C /* WatchHistoricalGlucose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F11D3C120DD80B3006E072C /* WatchHistoricalGlucose.swift */; };
- 4F20AE621E6B879C00D07A06 /* ReservoirVolumeHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEC71CD84CBB003C8C80 /* ReservoirVolumeHUDView.swift */; };
4F20AE631E6B87B100D07A06 /* ChartContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4313EDDF1D8A6BF90060FA79 /* ChartContainerView.swift */; };
4F2C15741E0209F500E160D4 /* NSTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897341CD2F7DE00223065 /* NSTimeInterval.swift */; };
4F2C15811E0495B200E160D4 /* WatchContext+WatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2C15801E0495B200E160D4 /* WatchContext+WatchApp.swift */; };
@@ -226,10 +285,9 @@
4F2C15851E075B8700E160D4 /* LoopUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F75288D1DFE1DC600C322D6 /* LoopUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
4F2C15931E09BF2C00E160D4 /* HUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2C15921E09BF2C00E160D4 /* HUDView.swift */; };
4F2C15951E09BF3C00E160D4 /* HUDView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F2C15941E09BF3C00E160D4 /* HUDView.xib */; };
- 4F2C15971E09E94E00E160D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4F2C15961E09E94E00E160D4 /* Assets.xcassets */; };
+ 4F2C15971E09E94E00E160D4 /* HUDAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4F2C15961E09E94E00E160D4 /* HUDAssets.xcassets */; };
4F2C159A1E0C9E5600E160D4 /* LoopUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F75288B1DFE1DC600C322D6 /* LoopUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4F526D611DF8D9A900A04910 /* NetBasal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F526D601DF8D9A900A04910 /* NetBasal.swift */; };
- 4F526D621DF9D95200A04910 /* NSBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430DA58D1D4AEC230097D1CA /* NSBundle.swift */; };
4F6663941E905FD2009E74FC /* ChartColorPalette+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F6663931E905FD2009E74FC /* ChartColorPalette+Loop.swift */; };
4F70C1E11DE8DCA7006380B7 /* StatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F70C1E01DE8DCA7006380B7 /* StatusViewController.swift */; };
4F70C1E41DE8DCA7006380B7 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F70C1E21DE8DCA7006380B7 /* MainInterface.storyboard */; };
@@ -241,13 +299,10 @@
4F7528941DFE1E9500C322D6 /* LoopUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F75288B1DFE1DC600C322D6 /* LoopUI.framework */; };
4F7528951DFE1E9B00C322D6 /* LoopUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F75288B1DFE1DC600C322D6 /* LoopUI.framework */; };
4F75289A1DFE1F6000C322D6 /* BasalRateHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEBF1CD6FCD8003C8C80 /* BasalRateHUDView.swift */; };
- 4F75289B1DFE1F6000C322D6 /* BatteryLevelHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEC91CD84DB7003C8C80 /* BatteryLevelHUDView.swift */; };
4F75289C1DFE1F6000C322D6 /* GlucoseHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4337615E1D52F487004A3647 /* GlucoseHUDView.swift */; };
- 4F75289D1DFE1F6000C322D6 /* BaseHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEBB1CD6DE6A003C8C80 /* BaseHUDView.swift */; };
4F75289E1DFE1F6000C322D6 /* LoopCompletionHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEBD1CD6E0CB003C8C80 /* LoopCompletionHUDView.swift */; };
4F7528A01DFE1F9D00C322D6 /* LoopStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438DADC71CDE8F8B007697A5 /* LoopStateView.swift */; };
4F7528A11DFE200B00C322D6 /* BasalStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43B371851CE583890013C5A6 /* BasalStateView.swift */; };
- 4F7528A21DFE200B00C322D6 /* LevelMaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FBEDD71D73843700B21F22 /* LevelMaskView.swift */; };
4F7528A51DFE208C00C322D6 /* NSTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439897341CD2F7DE00223065 /* NSTimeInterval.swift */; };
4F7528AA1DFE215100C322D6 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F526D5E1DF2459000A04910 /* HKUnit.swift */; };
4F75F00220FCFE8C00B5570E /* GlucoseChartScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F75F00120FCFE8C00B5570E /* GlucoseChartScene.swift */; };
@@ -266,88 +321,120 @@
4FB76FB91E8C42B000B39636 /* CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43649A621C7A347F00523D7F /* CollectionType.swift */; };
4FB76FBA1E8C42CE00B39636 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0B11E45C18400FF19A9 /* UIColor.swift */; };
4FB76FBB1E8C42CF00B39636 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0B11E45C18400FF19A9 /* UIColor.swift */; };
- 4FB76FC61E8C57B100B39636 /* StatusChartsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FB76FC51E8C57B100B39636 /* StatusChartsManager.swift */; };
+ 4FB76FC61E8C57B100B39636 /* ChartsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FB76FC51E8C57B100B39636 /* ChartsManager.swift */; };
4FB76FCE1E8C835D00B39636 /* ChartColorPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FB76FCD1E8C835D00B39636 /* ChartColorPalette.swift */; };
4FC8C8011DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC8C8001DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift */; };
4FC8C8021DEB943800A1452E /* NSUserDefaults+StatusExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC8C8001DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift */; };
4FDDD23720DC51DF00D04B16 /* LoopDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FDDD23620DC51DF00D04B16 /* LoopDataManager.swift */; };
- 4FF0F75E20E1E5D100FC6291 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E73471FF95A900069B5F7 /* PersistenceController.swift */; };
4FF4D0F81E1725B000846527 /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434F54561D287FDB002A9274 /* NibLoadable.swift */; };
- 4FF4D0F91E17268800846527 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */; };
4FF4D1001E18374700846527 /* WatchContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF4D0FF1E18374700846527 /* WatchContext.swift */; };
4FF4D1011E18375000846527 /* WatchContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF4D0FF1E18374700846527 /* WatchContext.swift */; };
7D23667D21250C7E0028B67D /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D23667C21250C7E0028B67D /* LocalizedString.swift */; };
- 7D2366E621250E0A0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D2366E421250E0A0028B67D /* InfoPlist.strings */; };
7D7076351FE06EDE004AC8EA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076371FE06EDE004AC8EA /* Localizable.strings */; };
- 7D70763A1FE06EDF004AC8EA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D70763C1FE06EDF004AC8EA /* InfoPlist.strings */; };
- 7D70763F1FE06EDF004AC8EA /* ckcomplication.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076411FE06EDF004AC8EA /* ckcomplication.strings */; };
7D7076451FE06EE0004AC8EA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076471FE06EE0004AC8EA /* InfoPlist.strings */; };
7D70764A1FE06EE1004AC8EA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D70764C1FE06EE1004AC8EA /* Localizable.strings */; };
7D70764F1FE06EE1004AC8EA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076511FE06EE1004AC8EA /* InfoPlist.strings */; };
- 7D7076541FE06EE2004AC8EA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076561FE06EE2004AC8EA /* InfoPlist.strings */; };
7D7076591FE06EE2004AC8EA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D70765B1FE06EE2004AC8EA /* Localizable.strings */; };
7D70765E1FE06EE3004AC8EA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076601FE06EE3004AC8EA /* Localizable.strings */; };
7D7076631FE06EE4004AC8EA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7076651FE06EE4004AC8EA /* Localizable.strings */; };
- 7D7076681FE0702F004AC8EA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D70766A1FE0702F004AC8EA /* InfoPlist.strings */; };
- 894F71E21FFEC4D8007D365C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 894F71E11FFEC4D8007D365C /* Assets.xcassets */; };
+ 7D9BEEF32335CF8D005DCFD6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D9BEEF52335CF8D005DCFD6 /* Localizable.strings */; };
+ 892A5D2A222EF60A008961AB /* MockKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 892A5D29222EF60A008961AB /* MockKit.framework */; };
+ 892A5D2C222EF60A008961AB /* MockKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 892A5D2B222EF60A008961AB /* MockKitUI.framework */; };
+ 892A5D59222F0A27008961AB /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892A5D58222F0A27008961AB /* Debug.swift */; };
+ 892A5D5B222F0D7C008961AB /* LoopTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 892A5D5A222F0D7C008961AB /* LoopTestingKit.framework */; };
+ 892A5D692230C41D008961AB /* RangeReplaceableCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892A5D682230C41D008961AB /* RangeReplaceableCollection.swift */; };
+ 892FB4CD22040104005293EC /* OverridePresetRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892FB4CC22040104005293EC /* OverridePresetRow.swift */; };
+ 892FB4CF220402C0005293EC /* OverrideSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892FB4CE220402C0005293EC /* OverrideSelectionController.swift */; };
+ 895FE0952201234000FCF18A /* OverrideSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895FE0942201234000FCF18A /* OverrideSelectionViewController.swift */; };
898ECA60218ABD17001E9D35 /* GlucoseChartScaler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 898ECA5E218ABD17001E9D35 /* GlucoseChartScaler.swift */; };
898ECA61218ABD17001E9D35 /* GlucoseChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 898ECA5F218ABD17001E9D35 /* GlucoseChartData.swift */; };
898ECA63218ABD21001E9D35 /* ComplicationChartManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 898ECA62218ABD21001E9D35 /* ComplicationChartManager.swift */; };
898ECA65218ABD9B001E9D35 /* CGRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 898ECA64218ABD9A001E9D35 /* CGRect.swift */; };
898ECA69218ABDA9001E9D35 /* CLKTextProvider+Compound.m in Sources */ = {isa = PBXBuildFile; fileRef = 898ECA67218ABDA8001E9D35 /* CLKTextProvider+Compound.m */; };
+ 89ADE13B226BFA0F0067222B /* TestingScenariosManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89ADE13A226BFA0F0067222B /* TestingScenariosManager.swift */; };
+ 89CA2B30226C0161004D9350 /* DirectoryObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89CA2B2F226C0161004D9350 /* DirectoryObserver.swift */; };
+ 89CA2B32226C18B8004D9350 /* TestingScenariosTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89CA2B31226C18B8004D9350 /* TestingScenariosTableViewController.swift */; };
+ 89CA2B3D226E6B13004D9350 /* LocalTestingScenariosManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89CA2B3C226E6B13004D9350 /* LocalTestingScenariosManager.swift */; };
+ 89E267FC2292456700A3F2AF /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E267FB2292456700A3F2AF /* FeatureFlags.swift */; };
+ 89E267FD2292456700A3F2AF /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E267FB2292456700A3F2AF /* FeatureFlags.swift */; };
+ 89E267FF229267DF00A3F2AF /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E267FE229267DF00A3F2AF /* Optional.swift */; };
+ 89E26800229267DF00A3F2AF /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E267FE229267DF00A3F2AF /* Optional.swift */; };
C10428971D17BAD400DD539A /* NightscoutUploadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C10428961D17BAD400DD539A /* NightscoutUploadKit.framework */; };
C10B28461EA9BA5E006EA1FC /* far_future_high_bg_forecast.json in Resources */ = {isa = PBXBuildFile; fileRef = C10B28451EA9BA5E006EA1FC /* far_future_high_bg_forecast.json */; };
C11C87DE1E21EAAD00BB71D3 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F526D5E1DF2459000A04910 /* HKUnit.swift */; };
+ C1265BEE231BF7F700652B84 /* DefaultAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43776F981B8022E90074EA36 /* DefaultAssets.xcassets */; };
C12F21A71DFA79CB00748193 /* recommend_temp_basal_very_low_end_in_range.json in Resources */ = {isa = PBXBuildFile; fileRef = C12F21A61DFA79CB00748193 /* recommend_temp_basal_very_low_end_in_range.json */; };
+ C13255D6223E7BE2008AF50C /* BolusProgressTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1F8B1DB223862D500DD66CF /* BolusProgressTableViewCell.xib */; };
+ C136AA2423109CC6008A320D /* LoopPlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16DA84122E8E112008624C2 /* LoopPlugins.swift */; };
C13BAD941E8009B000050CB5 /* NumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BFF0B31E45C1BE00FF19A9 /* NumberFormatter.swift */; };
C15713821DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */; };
+ C165B8CE23302C5D0004112E /* RemoteCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C165B8CD23302C5D0004112E /* RemoteCommand.swift */; };
+ C16DA84222E8E112008624C2 /* LoopPlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16DA84122E8E112008624C2 /* LoopPlugins.swift */; };
C178249A1E1999FA00D9D25C /* CaseCountable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824991E1999FA00D9D25C /* CaseCountable.swift */; };
C17824A01E19CF9800D9D25C /* GlucoseThresholdTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C178249F1E19CF9800D9D25C /* GlucoseThresholdTableViewController.swift */; };
C17824A31E19EAB600D9D25C /* recommend_temp_basal_start_very_low_end_high.json in Resources */ = {isa = PBXBuildFile; fileRef = C17824A21E19EAB600D9D25C /* recommend_temp_basal_start_very_low_end_high.json */; };
C17824A51E1AD4D100D9D25C /* BolusRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824A41E1AD4D100D9D25C /* BolusRecommendation.swift */; };
C17824A61E1AF91F00D9D25C /* BolusRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824A41E1AD4D100D9D25C /* BolusRecommendation.swift */; };
- C18852E22082AB1B00BECC8C /* RileyLinkKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C18852E12082AB1A00BECC8C /* RileyLinkKitUI.framework */; };
+ C1814B86225E507C008D2D8E /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1814B85225E507C008D2D8E /* Sequence.swift */; };
C18C8C511D5A351900E043FB /* NightscoutDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */; };
+ C1A3EED2235233E1007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */; };
+ C1A3EED423523551007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED323523551007672E3 /* DerivedAssets.xcassets */; };
+ C1A3EED523535FFF007672E3 /* DefaultAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 894F71E11FFEC4D8007D365C /* DefaultAssets.xcassets */; };
C1C0BE2A224C0FA000C03B4D /* SwiftCharts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */; };
C1C6591C1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json in Resources */ = {isa = PBXBuildFile; fileRef = C1C6591B1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json */; };
C1C73F0D1DE3D0270022FC89 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C1C73F0F1DE3D0270022FC89 /* InfoPlist.strings */; };
+ C1D289B522F90A52003FFBD9 /* BasalDeliveryState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D289B422F90A52003FFBD9 /* BasalDeliveryState.swift */; };
+ C1E2773E224177C000354103 /* ClockKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1E2773D224177C000354103 /* ClockKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+ C1E2774822433D7A00354103 /* MKRingProgressView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1E2774722433D7A00354103 /* MKRingProgressView.framework */; };
+ C1F8B243223E73FD00DD66CF /* BolusProgressTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F8B1D122375E4200DD66CF /* BolusProgressTableViewCell.swift */; };
+ C1FB428C217806A400FAB378 /* StateColorPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FB428B217806A300FAB378 /* StateColorPalette.swift */; };
+ C1FB428D21791D2500FAB378 /* PumpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C3B6F620BBCAA30026CAFA /* PumpManager.swift */; };
+ C1FB428F217921D600FAB378 /* PumpManagerUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FB428E217921D600FAB378 /* PumpManagerUI.swift */; };
+ C1FB4290217922A100FAB378 /* PumpManagerUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FB428E217921D600FAB378 /* PumpManagerUI.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- 432CF87C20D8B8990066B889 /* PBXContainerItemProxy */ = {
+ 43A943801B926B7B0051FA24 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 43776F841B8022E90074EA36 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 432CF87720D8B8380066B889;
- remoteInfo = Cartfile;
+ remoteGlobalIDString = 43A9437D1B926B7B0051FA24;
+ remoteInfo = "WatchApp Extension";
};
- 432CF87E20D8BC3B0066B889 /* PBXContainerItemProxy */ = {
+ 43A943921B926B7B0051FA24 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 43776F841B8022E90074EA36 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 432CF87720D8B8380066B889;
- remoteInfo = Cartfile;
+ remoteGlobalIDString = 43A943711B926B7B0051FA24;
+ remoteInfo = WatchApp;
};
- 432CF88020D8BC460066B889 /* PBXContainerItemProxy */ = {
+ 43D9000C21EB0BEA00AF44BF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 43776F841B8022E90074EA36 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 432CF87720D8B8380066B889;
- remoteInfo = Cartfile;
+ remoteGlobalIDString = 43D9FFCE21EAE05D00AF44BF;
+ remoteInfo = LoopCore;
};
- 43A943801B926B7B0051FA24 /* PBXContainerItemProxy */ = {
+ 43D9001221EB137A00AF44BF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 43776F841B8022E90074EA36 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 43A9437D1B926B7B0051FA24;
- remoteInfo = "WatchApp Extension";
+ remoteGlobalIDString = 43D9FFCE21EAE05D00AF44BF;
+ remoteInfo = LoopCore;
};
- 43A943921B926B7B0051FA24 /* PBXContainerItemProxy */ = {
+ 43D9FFB921EA9CA400AF44BF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 43776F841B8022E90074EA36 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 43A943711B926B7B0051FA24;
- remoteInfo = WatchApp;
+ remoteGlobalIDString = 4F75288A1DFE1DC600C322D6;
+ remoteInfo = LoopUI;
+ };
+ 43D9FFD421EAE05D00AF44BF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 43776F841B8022E90074EA36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 43D9FFCE21EAE05D00AF44BF;
+ remoteInfo = LoopCore;
};
43E2D9101D20C581004DA55F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
@@ -377,6 +464,27 @@
remoteGlobalIDString = 4F75288A1DFE1DC600C322D6;
remoteInfo = LoopUI;
};
+ A942E444225FD97F00DD4980 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 43776F841B8022E90074EA36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 43D9FFCE21EAE05D00AF44BF;
+ remoteInfo = LoopCore;
+ };
+ A942E446225FD9A300DD4980 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 43776F841B8022E90074EA36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 43D9FFCE21EAE05D00AF44BF;
+ remoteInfo = LoopCore;
+ };
+ C117ED70232EDB3200DA57CD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 43776F841B8022E90074EA36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 43D9001A21EB209400AF44BF;
+ remoteInfo = "LoopCore-watchOS";
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -409,6 +517,7 @@
dstSubfolderSpec = 10;
files = (
4F2C159A1E0C9E5600E160D4 /* LoopUI.framework in Embed Frameworks */,
+ 43D9FFD721EAE05D00AF44BF /* LoopCore.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -419,6 +528,19 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
+ 43C05CB221EBD88A006FB252 /* LoopCore.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 43D9FFDF21EAE3C600AF44BF /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 43D9FFE021EAE3E500AF44BF /* LoopUI.framework in Embed Frameworks */,
+ 43D9FFE121EAE3E500AF44BF /* LoopCore.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -429,7 +551,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
- 43E2D9171D2226BD004DA55F /* LoopKit.framework in CopyFiles */,
+ 4345E40021F051DD009E00E5 /* LoopCore.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -447,6 +569,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 2E497FE822554CE3008E41F6 /* SpikeClient.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SpikeClient.framework; path = Carthage/Build/iOS/SpikeClient.framework; sourceTree = ""; };
+ 2E497FE922554CE3008E41F6 /* SpikeClientUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SpikeClientUI.framework; path = Carthage/Build/iOS/SpikeClientUI.framework; sourceTree = ""; };
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 = ""; };
@@ -454,7 +578,6 @@
430B298D2041F56500BA9F93 /* GlucoseThreshold.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseThreshold.swift; sourceTree = ""; };
430B29922041F5B200BA9F93 /* UserDefaults+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Loop.swift"; sourceTree = ""; };
430B29942041F5CB00BA9F93 /* LoopSettings+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LoopSettings+Loop.swift"; sourceTree = ""; };
- 430C1ABC1E5568A80067F1AE /* StatusChartsManager+LoopKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StatusChartsManager+LoopKit.swift"; sourceTree = ""; };
430D85881F44037000AF2D4F /* HUDViewTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUDViewTableViewCell.swift; sourceTree = ""; };
430DA58D1D4AEC230097D1CA /* NSBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSBundle.swift; sourceTree = ""; };
4311FB9A1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleSubtitleTextFieldTableViewCell.swift; sourceTree = ""; };
@@ -467,7 +590,6 @@
4328E0151CFBE1DA00E199AA /* ActionHUDController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionHUDController.swift; sourceTree = ""; };
4328E0161CFBE1DA00E199AA /* BolusInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BolusInterfaceController.swift; sourceTree = ""; };
4328E01D1CFBE25F00E199AA /* AddCarbsInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddCarbsInterfaceController.swift; sourceTree = ""; };
- 4328E0201CFBE2C500E199AA /* IdentifiableClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentifiableClass.swift; sourceTree = ""; };
4328E0221CFBE2C500E199AA /* CLKComplicationTemplate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLKComplicationTemplate.swift; sourceTree = ""; };
4328E0231CFBE2C500E199AA /* NSUserDefaults+WatchApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSUserDefaults+WatchApp.swift"; sourceTree = ""; };
4328E0241CFBE2C500E199AA /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; };
@@ -480,21 +602,28 @@
433EA4C31D9F71C800CD78FB /* CommandResponseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandResponseViewController.swift; sourceTree = ""; };
4341F4EA1EDB92AC001C936B /* LogglyService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogglyService.swift; sourceTree = ""; };
43441A9B1EDB34810087958C /* StatusExtensionContext+LoopKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StatusExtensionContext+LoopKit.swift"; sourceTree = ""; };
- 4344627F20A7A37400C4BE6F /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS4.3.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
- 4344628120A7A37E00C4BE6F /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS4.3.sdk/System/Library/Frameworks/CoreBluetooth.framework; sourceTree = DEVELOPER_DIR; };
- 4344628320A7A3BE00C4BE6F /* LoopKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKit.framework; path = Carthage/Build/watchOS/LoopKit.framework; sourceTree = ""; };
- 4344628420A7A3BE00C4BE6F /* CGMBLEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CGMBLEKit.framework; path = Carthage/Build/watchOS/CGMBLEKit.framework; sourceTree = ""; };
+ 4344628120A7A37E00C4BE6F /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/System/Library/Frameworks/CoreBluetooth.framework; sourceTree = DEVELOPER_DIR; };
+ 4344628320A7A3BE00C4BE6F /* LoopKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4344628420A7A3BE00C4BE6F /* CGMBLEKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CGMBLEKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4344628D20A7ADD100C4BE6F /* UserDefaults+CGM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+CGM.swift"; sourceTree = ""; };
4344629120A7C19800C4BE6F /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = ""; };
+ 4345E3F721F03D2A009E00E5 /* DatesAndNumberCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatesAndNumberCell.swift; sourceTree = ""; };
+ 4345E3F921F0473B009E00E5 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = ""; };
+ 4345E3FD21F04A50009E00E5 /* DateIntervalFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateIntervalFormatter.swift; sourceTree = ""; };
+ 4345E40321F68AD9009E00E5 /* TextRowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRowController.swift; sourceTree = ""; };
+ 4345E40521F68E18009E00E5 /* CarbEntryListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEntryListController.swift; sourceTree = ""; };
4346D1E61C77F5FE00ABAFE3 /* ChartTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ChartTableViewCell.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
- 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCharts.framework; path = Carthage/Build/iOS/SwiftCharts.framework; sourceTree = SOURCE_ROOT; };
- 434AB0B11CBB4C3300422F4A /* RileyLinkBLEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RileyLinkBLEKit.framework; path = Carthage/Build/iOS/RileyLinkBLEKit.framework; sourceTree = SOURCE_ROOT; };
+ 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftCharts.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 434A9F9823124B210047C077 /* BolusConfirmationScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusConfirmationScene.swift; sourceTree = ""; };
434F54561D287FDB002A9274 /* NibLoadable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoadable.swift; sourceTree = ""; };
- 434FB6451D68F1CD007B9C70 /* Amplitude.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Amplitude.framework; path = Carthage/Build/iOS/Amplitude.framework; sourceTree = SOURCE_ROOT; };
+ 434FB6451D68F1CD007B9C70 /* Amplitude.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Amplitude.framework; sourceTree = BUILT_PRODUCTS_DIR; };
434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentifiableClass.swift; sourceTree = ""; };
434FF1ED1CF27EEF000DB779 /* UITableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; };
- 43523EDA1CC35083001850F1 /* RileyLinkKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RileyLinkKit.framework; path = Carthage/Build/iOS/RileyLinkKit.framework; sourceTree = SOURCE_ROOT; };
- 435400301C9F744E00D5819C /* BolusSuggestionUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BolusSuggestionUserInfo.swift; sourceTree = ""; };
+ 43511CDF21FD80E400566C63 /* RetrospectiveCorrection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetrospectiveCorrection.swift; sourceTree = ""; };
+ 43511CE021FD80E400566C63 /* StandardRetrospectiveCorrection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StandardRetrospectiveCorrection.swift; sourceTree = ""; };
+ 43511CED220FC61700566C63 /* HUDRowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDRowController.swift; sourceTree = ""; };
+ 43517914230A07100072ECC0 /* NumberFormatter+WatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+WatchApp.swift"; sourceTree = ""; };
+ 43517916230A0E1A0072ECC0 /* WKInterfaceLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceLabel.swift; sourceTree = ""; };
435400331C9F878D00D5819C /* SetBolusUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetBolusUserInfo.swift; sourceTree = ""; };
435CB6221F37967800C320C7 /* InsulinModelSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsulinModelSettingsViewController.swift; sourceTree = ""; };
435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExponentialInsulinModelPreset.swift; sourceTree = ""; };
@@ -504,45 +633,41 @@
4369618F1F19C86400447E89 /* ChartPointsContextFillLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPointsContextFillLayer.swift; sourceTree = ""; };
436A0DA41D236A2A00104B24 /* LoopError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopError.swift; sourceTree = ""; };
436D9BF71F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommended_temp_start_low_end_just_above_range.json; sourceTree = ""; };
- 43709AE920DF3F8200F941B3 /* MinimedKitUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MinimedKitUI.framework; path = Carthage/Build/iOS/MinimedKitUI.framework; sourceTree = ""; };
4372E486213C86240068E043 /* SampleValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleValue.swift; sourceTree = ""; };
4372E48A213CB5F00068E043 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = ""; };
4372E48F213CFCE70068E043 /* LoopSettingsUserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopSettingsUserInfo.swift; sourceTree = ""; };
4372E495213DCDD30068E043 /* GlucoseChartValueHashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseChartValueHashable.swift; sourceTree = ""; };
4374B5EE209D84BE00D17AA8 /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = ""; };
- 4374B5F1209D897600D17AA8 /* Locked.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locked.swift; sourceTree = ""; };
4374B5F3209D89A900D17AA8 /* TextFieldTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldTableViewCell.swift; sourceTree = ""; };
43776F8C1B8022E90074EA36 /* Loop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Loop.app; sourceTree = BUILT_PRODUCTS_DIR; };
43776F8F1B8022E90074EA36 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
43776F961B8022E90074EA36 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
- 43776F981B8022E90074EA36 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 43776F981B8022E90074EA36 /* DefaultAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DefaultAssets.xcassets; sourceTree = ""; };
43776F9B1B8022E90074EA36 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
43785E922120A01B0057DED1 /* NewCarbEntryIntent+Loop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NewCarbEntryIntent+Loop.swift"; sourceTree = ""; };
43785E952120E4010057DED1 /* INRelevantShortcutStore+Loop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "INRelevantShortcutStore+Loop.swift"; sourceTree = ""; };
43785E9A2120E7060057DED1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; };
43785E9F2122774A0057DED1 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = ""; };
43785EA12122774B0057DED1 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Intents.strings; sourceTree = ""; };
- 43785EA32122774B0057DED1 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Intents.strings; sourceTree = ""; };
- 4379CFEF21112CF700AADC79 /* ShareClientUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ShareClientUI.framework; path = Carthage/Build/iOS/ShareClientUI.framework; sourceTree = SOURCE_ROOT; };
- 437AFEE6203688CF008C4892 /* LoopKitUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKitUI.framework; path = Carthage/Build/iOS/LoopKitUI.framework; sourceTree = ""; };
- 437CEEBB1CD6DE6A003C8C80 /* BaseHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseHUDView.swift; sourceTree = ""; };
+ 4379CFEF21112CF700AADC79 /* ShareClientUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClientUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 437AFEE6203688CF008C4892 /* LoopKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
437CEEBD1CD6E0CB003C8C80 /* LoopCompletionHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopCompletionHUDView.swift; sourceTree = ""; };
437CEEBF1CD6FCD8003C8C80 /* BasalRateHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasalRateHUDView.swift; sourceTree = ""; };
- 437CEEC71CD84CBB003C8C80 /* ReservoirVolumeHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReservoirVolumeHUDView.swift; sourceTree = ""; };
- 437CEEC91CD84DB7003C8C80 /* BatteryLevelHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryLevelHUDView.swift; sourceTree = ""; };
437CEEE31CDE5C0A003C8C80 /* UIImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; };
437D9BA11D7B5203007245E8 /* Loop.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Loop.xcconfig; sourceTree = ""; };
437D9BA21D7BC977007245E8 /* PredictionTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictionTableViewController.swift; sourceTree = ""; };
+ 437DE503229C8375003B1074 /* copy-frameworks.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "copy-frameworks.sh"; sourceTree = ""; };
438172D81F4E9E37003C3328 /* NewPumpEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPumpEvent.swift; sourceTree = ""; };
438849E91D297CB6003B3F23 /* NightscoutService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutService.swift; sourceTree = ""; };
438849EB1D29EC34003B3F23 /* AmplitudeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplitudeService.swift; sourceTree = ""; };
438849ED1D2A1EBB003B3F23 /* MLabService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MLabService.swift; sourceTree = ""; };
438991661E91B563000EEF90 /* ChartPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPoint.swift; sourceTree = ""; };
4389916A1E91B689000EEF90 /* ChartSettings+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ChartSettings+Loop.swift"; sourceTree = ""; };
- 438A95A71D8B9B24009D12E1 /* CGMBLEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CGMBLEKit.framework; path = Carthage/Build/iOS/CGMBLEKit.framework; sourceTree = SOURCE_ROOT; };
+ 438A95A71D8B9B24009D12E1 /* CGMBLEKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CGMBLEKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
438D42F81D7C88BC003244B0 /* PredictionInputEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictionInputEffect.swift; sourceTree = ""; };
438D42FA1D7D11A4003244B0 /* PredictionInputEffectTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictionInputEffectTableViewCell.swift; sourceTree = ""; };
438DADC71CDE8F8B007697A5 /* LoopStateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopStateView.swift; sourceTree = ""; };
+ 439706E522D2E84900C81566 /* PredictionSettingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PredictionSettingTableViewCell.swift; sourceTree = ""; };
439897341CD2F7DE00223065 /* NSTimeInterval.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTimeInterval.swift; sourceTree = ""; };
439897361CD2F80600223065 /* AnalyticsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnalyticsManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
439A7941211F631C0041B75F /* RootNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootNavigationController.swift; sourceTree = ""; };
@@ -551,7 +676,7 @@
43A51E1E1EB6D62A000736CC /* CarbAbsorptionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbAbsorptionViewController.swift; sourceTree = ""; };
43A51E201EB6DBDD000736CC /* ChartsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartsTableViewController.swift; sourceTree = ""; };
43A567681C94880B00334FAC /* LoopDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = LoopDataManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
- 43A8EC6E210E622600A81379 /* CGMBLEKitUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CGMBLEKitUI.framework; path = Carthage/Build/iOS/CGMBLEKitUI.framework; sourceTree = ""; };
+ 43A8EC6E210E622600A81379 /* CGMBLEKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CGMBLEKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
43A943721B926B7B0051FA24 /* WatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
43A943751B926B7B0051FA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; };
43A9437E1B926B7B0051FA24 /* WatchApp Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "WatchApp Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -563,23 +688,32 @@
43A943911B926B7B0051FA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
43B260481ED248FB008CAA77 /* CarbEntryTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryTableViewCell.swift; sourceTree = ""; };
43B371851CE583890013C5A6 /* BasalStateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasalStateView.swift; sourceTree = ""; };
- 43B371871CE597D10013C5A6 /* ShareClient.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ShareClient.framework; path = Carthage/Build/iOS/ShareClient.framework; sourceTree = SOURCE_ROOT; };
+ 43B371871CE597D10013C5A6 /* ShareClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
43BFF0B11E45C18400FF19A9 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; };
43BFF0B31E45C1BE00FF19A9 /* NumberFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatter.swift; sourceTree = ""; };
43BFF0BB1E45C80600FF19A9 /* UIColor+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Loop.swift"; sourceTree = ""; };
43BFF0BE1E45C8EA00FF19A9 /* UIColor+Widget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Widget.swift"; sourceTree = ""; };
43BFF0C31E4659E700FF19A9 /* UIColor+HIG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+HIG.swift"; sourceTree = ""; };
- 43BFF0C81E465B0A00FF19A9 /* StateColorPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateColorPalette.swift; sourceTree = ""; };
- 43BFF0CA1E466C0900FF19A9 /* StateColorPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateColorPalette.swift; sourceTree = ""; };
43BFF0CC1E466C8400FF19A9 /* StateColorPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateColorPalette.swift; sourceTree = ""; };
+ 43C05CB021EBBDB9006FB252 /* TimeInRangeLesson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInRangeLesson.swift; sourceTree = ""; };
+ 43C05CB421EBE274006FB252 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; };
+ 43C05CB721EBEA54006FB252 /* HKUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = ""; };
+ 43C05CBC21EBF77D006FB252 /* LessonsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LessonsViewController.swift; sourceTree = ""; };
+ 43C05CBF21EBFFA4006FB252 /* Lesson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lesson.swift; sourceTree = ""; };
+ 43C05CC121EC06E4006FB252 /* LessonConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LessonConfigurationViewController.swift; sourceTree = ""; };
+ 43C05CC921EC382B006FB252 /* NumberEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberEntry.swift; sourceTree = ""; };
43C094491CACCC73001F6403 /* NotificationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = ""; };
- 43C246A71D89990F0031F8D1 /* Crypto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crypto.framework; path = Carthage/Build/iOS/Crypto.framework; sourceTree = SOURCE_ROOT; };
+ 43C246A71D89990F0031F8D1 /* Crypto.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Crypto.framework; sourceTree = BUILT_PRODUCTS_DIR; };
43C2FAE01EB656A500364AFF /* GlucoseEffectVelocity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseEffectVelocity.swift; sourceTree = ""; };
43C3B6EB20B650A80026CAFA /* SettingsImageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsImageTableViewCell.swift; sourceTree = ""; };
43C3B6F620BBCAA30026CAFA /* PumpManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpManager.swift; sourceTree = ""; };
43C513181E864C4E001547C7 /* GlucoseRangeSchedule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseRangeSchedule.swift; sourceTree = ""; };
+ 43C5F256222C7B7200905D10 /* TimeComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeComponents.swift; sourceTree = ""; };
+ 43C5F259222C921B00905D10 /* OSLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = ""; };
+ 43C728F4222266F000C62969 /* ModalDayLesson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalDayLesson.swift; sourceTree = ""; };
+ 43C728F62222700000C62969 /* DateIntervalEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateIntervalEntry.swift; sourceTree = ""; };
+ 43C728F8222A448700C62969 /* DayCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayCalculator.swift; sourceTree = ""; };
43C98058212A799E003B5D17 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = ""; };
- 43CA93361CB98079000026B5 /* MinimedKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MinimedKit.framework; path = Carthage/Build/iOS/MinimedKit.framework; sourceTree = SOURCE_ROOT; };
43CB2B2A1D924D450079823D /* WCSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WCSession.swift; sourceTree = ""; };
43CE7CDD1CA8B63E003CC1B0 /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; };
43CEE6E51E56AFD400CB9116 /* NightscoutUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutUploader.swift; sourceTree = ""; };
@@ -587,13 +721,31 @@
43D381611EBD9759007F8C8F /* HeaderValuesTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderValuesTableViewCell.swift; sourceTree = ""; };
43D533BB1CFD1DD7009E3085 /* WatchApp Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "WatchApp Extension.entitlements"; sourceTree = ""; };
43D848AF1E7DCBE100DADCBC /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; };
+ 43D9002A21EB209400AF44BF /* LoopCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LoopCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 43D9002C21EB225D00AF44BF /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
+ 43D9003221EB258C00AF44BF /* InsulinModelSettings+Loop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinModelSettings+Loop.swift"; sourceTree = ""; };
+ 43D9F81721EC51CC000578CD /* DateEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateEntry.swift; sourceTree = ""; };
+ 43D9F81921EC593C000578CD /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; };
+ 43D9F81D21EF0609000578CD /* NumberRangeEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberRangeEntry.swift; sourceTree = ""; };
+ 43D9F81F21EF0906000578CD /* NSNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSNumber.swift; sourceTree = ""; };
+ 43D9F82121EF0A7A000578CD /* QuantityRangeEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuantityRangeEntry.swift; sourceTree = ""; };
+ 43D9F82321EFF1AB000578CD /* LessonResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LessonResultsViewController.swift; sourceTree = ""; };
+ 43D9FFA221EA9A0C00AF44BF /* Learn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Learn.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 43D9FFA421EA9A0C00AF44BF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 43D9FFA921EA9A0C00AF44BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 43D9FFAB21EA9A0F00AF44BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 43D9FFAE21EA9A0F00AF44BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 43D9FFB021EA9A0F00AF44BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 43D9FFB521EA9B0100AF44BF /* Learn.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Learn.entitlements; sourceTree = ""; };
+ 43D9FFBF21EAB22E00AF44BF /* DataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = ""; };
+ 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LoopCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 43D9FFD121EAE05D00AF44BF /* LoopCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoopCore.h; sourceTree = ""; };
+ 43D9FFD221EAE05D00AF44BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
43DACFFF20A2736F000F8529 /* PersistedPumpEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistedPumpEvent.swift; sourceTree = ""; };
43DBF04B1C93B8D700B3C386 /* BolusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BolusViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
43DBF0521C93EC8200B3C386 /* DeviceDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DeviceDataManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
43DBF0581C93F73800B3C386 /* CarbEntryTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryTableViewController.swift; sourceTree = ""; };
43DE92581C5479E4001FFDE1 /* CarbEntryUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryUserInfo.swift; sourceTree = ""; };
- 43DE92601C555C26001FFDE1 /* AbsorptionTimeType+CarbKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AbsorptionTimeType+CarbKit.swift"; sourceTree = ""; };
- 43E0F0A41E46D1670064F7CE /* LevelHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LevelHUDView.swift; sourceTree = ""; };
43E2D8C71D208D5B004DA55F /* KeychainManager+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeychainManager+Loop.swift"; sourceTree = ""; };
43E2D8C91D20B9E7004DA55F /* KeychainManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainManagerTests.swift; sourceTree = ""; };
43E2D8D11D20BF42004DA55F /* DoseMathTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DoseMathTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -622,9 +774,20 @@
43F5C2DA1B92A5E1003EB13D /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SettingsTableViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
43F64DD81D9C92C900D24DC6 /* TitleSubtitleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleSubtitleTableViewCell.swift; sourceTree = ""; };
43F78D251C8FC000002152D1 /* DoseMath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoseMath.swift; sourceTree = ""; };
- 43F78D4B1C914197002152D1 /* LoopKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKit.framework; path = Carthage/Build/iOS/LoopKit.framework; sourceTree = SOURCE_ROOT; };
- 43FBEDD71D73843700B21F22 /* LevelMaskView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LevelMaskView.swift; sourceTree = ""; };
- 4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = G4ShareSpy.framework; path = Carthage/Build/iOS/G4ShareSpy.framework; sourceTree = SOURCE_ROOT; };
+ 43F78D4B1C914197002152D1 /* LoopKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 43F89CA222BDFBBC006BB54E /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; };
+ 43FCEEA8221A615B0013DD30 /* StatusChartsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusChartsManager.swift; sourceTree = ""; };
+ 43FCEEAA221A61B40013DD30 /* IOBChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOBChart.swift; sourceTree = ""; };
+ 43FCEEAC221A66780013DD30 /* DateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatter.swift; sourceTree = ""; };
+ 43FCEEAE221A67A70013DD30 /* NumberFormatter+Charts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+Charts.swift"; sourceTree = ""; };
+ 43FCEEB0221A863E0013DD30 /* StatusChartsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusChartsManager.swift; sourceTree = ""; };
+ 43FCEEB2221BC3B60013DD30 /* DoseChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoseChart.swift; sourceTree = ""; };
+ 43FCEEB4221BCA020013DD30 /* COBChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = COBChart.swift; sourceTree = ""; };
+ 43FCEEB6221BCD160013DD30 /* InsulinModelChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsulinModelChart.swift; sourceTree = ""; };
+ 43FCEEB8221BCF790013DD30 /* GlucoseChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseChart.swift; sourceTree = ""; };
+ 43FCEEBA22211C860013DD30 /* CarbEffectChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEffectChart.swift; sourceTree = ""; };
+ 43FCEEBC22212DD50013DD30 /* PredictedGlucoseChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PredictedGlucoseChart.swift; sourceTree = ""; };
+ 4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = G4ShareSpy.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4F08DE7C1E7BB6E5006741EA /* ChartAxisValueDoubleLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisValueDoubleLog.swift; sourceTree = ""; };
4F08DE7D1E7BB6E5006741EA /* ChartAxisValueDoubleUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisValueDoubleUnit.swift; sourceTree = ""; };
4F08DE801E7BB6F1006741EA /* CGPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGPoint.swift; sourceTree = ""; };
@@ -636,7 +799,7 @@
4F2C15801E0495B200E160D4 /* WatchContext+WatchApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WatchContext+WatchApp.swift"; sourceTree = ""; };
4F2C15921E09BF2C00E160D4 /* HUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUDView.swift; sourceTree = ""; };
4F2C15941E09BF3C00E160D4 /* HUDView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HUDView.xib; sourceTree = ""; };
- 4F2C15961E09E94E00E160D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 4F2C15961E09E94E00E160D4 /* HUDAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HUDAssets.xcassets; sourceTree = ""; };
4F526D5E1DF2459000A04910 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = ""; };
4F526D601DF8D9A900A04910 /* NetBasal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetBasal.swift; sourceTree = ""; };
4F6663931E905FD2009E74FC /* ChartColorPalette+Loop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ChartColorPalette+Loop.swift"; sourceTree = ""; };
@@ -656,179 +819,240 @@
4F7E8AC420E2AB9600AEA65E /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; };
4F7E8AC620E2AC0300AEA65E /* WatchPredictedGlucose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchPredictedGlucose.swift; sourceTree = ""; };
4F82654F20E69F9A0031A8F5 /* HUDInterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDInterfaceController.swift; sourceTree = ""; };
- 4FB76FC51E8C57B100B39636 /* StatusChartsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusChartsManager.swift; sourceTree = ""; };
+ 4FB76FC51E8C57B100B39636 /* ChartsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartsManager.swift; sourceTree = ""; };
4FB76FCD1E8C835D00B39636 /* ChartColorPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartColorPalette.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 = ""; };
4FF4D0FF1E18374700846527 /* WatchContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchContext.swift; sourceTree = ""; };
4FFEDFBE20E5CF22000BFC58 /* ChartHUDController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartHUDController.swift; sourceTree = ""; };
- 7D199D92212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/LaunchScreen.strings; sourceTree = ""; };
7D199D93212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Main.strings; sourceTree = ""; };
7D199D94212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/MainInterface.strings; sourceTree = ""; };
7D199D95212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Interface.strings; sourceTree = ""; };
7D199D96212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; };
7D199D97212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
- 7D199D98212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
7D199D99212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; };
7D199D9A212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; };
- 7D199D9B212A067600241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
- 7D199D9C212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
7D199D9D212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; };
- 7D199D9E212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/ckcomplication.strings; sourceTree = ""; };
- 7D199D9F212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
- 7D199DA0212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
- 7D199DA1212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; };
7D199DA2212A067700241026 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; };
7D23667521250BE30028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; };
7D23667621250BF70028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
- 7D23667721250C280028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
7D23667821250C2D0028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; };
7D23667921250C440028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; };
7D23667A21250C480028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
7D23667C21250C7E0028B67D /* LocalizedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LocalizedString.swift; path = LoopUI/Common/LocalizedString.swift; sourceTree = SOURCE_ROOT; };
7D23667E21250CAC0028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
7D23667F21250CB80028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; };
- 7D23668021250CBE0028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
- 7D23668121250CC50028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/ckcomplication.strings; sourceTree = ""; };
- 7D23668221250CF60028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; };
7D23668321250CFB0028B67D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; };
- 7D23668421250D180028B67D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LaunchScreen.strings; sourceTree = ""; };
7D23668521250D180028B67D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Main.strings; sourceTree = "