diff --git a/Cartfile b/Cartfile index 7297cae631..3df034bcf5 100644 --- a/Cartfile +++ b/Cartfile @@ -1,7 +1,7 @@ -github "loudnate/LoopKit" ~> 0.8.0 -github "loudnate/xDripG5" ~> 0.7.0 +github "LoopKit/LoopKit" ~> 1.0 +github "LoopKit/xDripG5" ~> 0.8.0 github "i-schuetz/SwiftCharts" ~> 0.5.0 github "mddub/dexcom-share-client-swift" ~> 0.2.0 github "mddub/G4ShareSpy" ~> 0.3.0 -github "ps2/rileylink_ios" ~> 0.12.3 +github "ps2/rileylink_ios" ~> 0.12.4 github "amplitude/Amplitude-iOS" ~> 3.8.5 diff --git a/Cartfile.resolved b/Cartfile.resolved index 47057d27e5..7a9e1ed990 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,7 +1,7 @@ github "amplitude/Amplitude-iOS" "v3.8.5" github "mddub/G4ShareSpy" "v0.3.0" -github "loudnate/LoopKit" "v0.8.0" +github "LoopKit/LoopKit" "v1.0" github "i-schuetz/SwiftCharts" "0.5" github "mddub/dexcom-share-client-swift" "v0.2.0" -github "ps2/rileylink_ios" "v0.12.3" -github "loudnate/xDripG5" "v0.7.0" +github "ps2/rileylink_ios" "v0.12.4" +github "LoopKit/xDripG5" "v0.8.0" diff --git a/Carthage/Build/iOS/CarbKit.framework/CarbKit b/Carthage/Build/iOS/CarbKit.framework/CarbKit index ca00023a19..6d5f2b2403 100755 Binary files a/Carthage/Build/iOS/CarbKit.framework/CarbKit and b/Carthage/Build/iOS/CarbKit.framework/CarbKit differ diff --git a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/CarbEntryEditViewController.nib b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/CarbEntryEditViewController.nib index 4fa8ae02b3..36cd1f5419 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/CarbEntryEditViewController.nib and b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/CarbEntryEditViewController.nib differ diff --git a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/LyL-9U-twn-view-9Ci-XW-6nA.nib b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/LyL-9U-twn-view-9Ci-XW-6nA.nib index dc16914803..8339510294 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/LyL-9U-twn-view-9Ci-XW-6nA.nib and b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/LyL-9U-twn-view-9Ci-XW-6nA.nib differ diff --git a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UINavigationController-wgu-gT-TgV.nib b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UINavigationController-wgu-gT-TgV.nib index 8c32e4f60a..2d1359d292 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UINavigationController-wgu-gT-TgV.nib and b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UINavigationController-wgu-gT-TgV.nib differ diff --git a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UITableViewController-rUL-yg-cFX.nib b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UITableViewController-rUL-yg-cFX.nib index 180e336153..8921eea59a 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UITableViewController-rUL-yg-cFX.nib and b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/UITableViewController-rUL-yg-cFX.nib differ diff --git a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/rUL-yg-cFX-view-b1s-8o-0Wp.nib b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/rUL-yg-cFX-view-b1s-8o-0Wp.nib index 2c7ee61a4d..083a2d6c9b 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/rUL-yg-cFX-view-b1s-8o-0Wp.nib and b/Carthage/Build/iOS/CarbKit.framework/CarbKit.storyboardc/rUL-yg-cFX-view-b1s-8o-0Wp.nib differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Info.plist b/Carthage/Build/iOS/CarbKit.framework/Info.plist index 81b8599836..1844064a2c 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Info.plist and b/Carthage/Build/iOS/CarbKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftdoc b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftdoc index cada112be5..3783ec7ed9 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftdoc and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftdoc differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftmodule index eb704c0afc..a3b38359ab 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftdoc b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftdoc index 93b7d551dc..4a350d7102 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftdoc and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftdoc differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftmodule index 3148d2b8d1..6ce7d414e1 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftdoc b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftdoc index eaeccda461..fce6bf726c 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftdoc and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftdoc differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftmodule index 310816e569..13f40a903c 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftdoc b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftdoc index c5e8887818..c556676121 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftdoc and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftdoc differ diff --git a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftmodule index 2f26bb9b7c..df0462a56e 100644 Binary files a/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/CarbKit.framework/Modules/CarbKit.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/Crypto.framework/Crypto b/Carthage/Build/iOS/Crypto.framework/Crypto index c11ace80b4..515409dc94 100755 Binary files a/Carthage/Build/iOS/Crypto.framework/Crypto and b/Carthage/Build/iOS/Crypto.framework/Crypto differ diff --git a/Carthage/Build/iOS/Crypto.framework/Info.plist b/Carthage/Build/iOS/Crypto.framework/Info.plist index 795171099a..0c4c2d2b1b 100644 Binary files a/Carthage/Build/iOS/Crypto.framework/Info.plist and b/Carthage/Build/iOS/Crypto.framework/Info.plist differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/GlucoseKit b/Carthage/Build/iOS/GlucoseKit.framework/GlucoseKit index eb75d87e9a..a90502a447 100755 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/GlucoseKit and b/Carthage/Build/iOS/GlucoseKit.framework/GlucoseKit differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Info.plist b/Carthage/Build/iOS/GlucoseKit.framework/Info.plist index 0af20707f0..671eb761a5 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Info.plist and b/Carthage/Build/iOS/GlucoseKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftdoc b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftdoc index bfa6d4e1f4..aee5436928 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftdoc and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftdoc differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftmodule index 5befcf9d8a..ce34a37381 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftdoc b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftdoc index 3de0d7e03c..477e62dc62 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftdoc and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftdoc differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftmodule index d908f1c87d..24f9820a02 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftdoc b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftdoc index 9fc1492ad2..79a164bf87 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftdoc and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftdoc differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftmodule index 3e5b89c37c..77425c8934 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftdoc b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftdoc index e06da3450b..9ba2351185 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftdoc and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftdoc differ diff --git a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftmodule index 073dd94693..0877df391d 100644 Binary files a/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/GlucoseKit.framework/Modules/GlucoseKit.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/UITableViewController-jGX-GA-nlH.nib b/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/UITableViewController-jGX-GA-nlH.nib index 3a9ed1e85d..1f21b1f15e 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/UITableViewController-jGX-GA-nlH.nib and b/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/UITableViewController-jGX-GA-nlH.nib differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/jGX-GA-nlH-view-ccM-3y-LQM.nib b/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/jGX-GA-nlH-view-ccM-3y-LQM.nib index 7f0193f96f..e64f446cec 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/jGX-GA-nlH-view-ccM-3y-LQM.nib and b/Carthage/Build/iOS/InsulinKit.framework/Base.lproj/InsulinKit.storyboardc/jGX-GA-nlH-view-ccM-3y-LQM.nib differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Info.plist b/Carthage/Build/iOS/InsulinKit.framework/Info.plist index 36313c0887..9d011adb87 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Info.plist and b/Carthage/Build/iOS/InsulinKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/InsulinKit b/Carthage/Build/iOS/InsulinKit.framework/InsulinKit index a2a7f0babc..93158889c1 100755 Binary files a/Carthage/Build/iOS/InsulinKit.framework/InsulinKit and b/Carthage/Build/iOS/InsulinKit.framework/InsulinKit differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftdoc b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftdoc index 73aa4d140b..44b1857f5c 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftdoc and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftdoc differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftmodule index 410dec66b0..92670f037e 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftdoc b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftdoc index 6a1c02bb3f..1610d1b463 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftdoc and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftdoc differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftmodule index 6935487b61..efb22bebc4 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftdoc b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftdoc index b57998c820..edabedbff5 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftdoc and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftdoc differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftmodule index 0f381cff0d..64aab0ec7b 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftdoc b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftdoc index 7dea37d202..4cae4d12b1 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftdoc and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftdoc differ diff --git a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftmodule index 6b9425df28..4e14d47c98 100644 Binary files a/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/InsulinKit.framework/Modules/InsulinKit.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/LoopKit.framework/Assets.car b/Carthage/Build/iOS/LoopKit.framework/Assets.car index 8e9717d107..fa60658660 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/Assets.car and b/Carthage/Build/iOS/LoopKit.framework/Assets.car differ diff --git a/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeOverrideTableViewCell.nib b/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeOverrideTableViewCell.nib index 74f73984fa..e129d5aee3 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeOverrideTableViewCell.nib and b/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeOverrideTableViewCell.nib differ diff --git a/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeTableViewCell.nib b/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeTableViewCell.nib index 9842e0162b..4b74b36941 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeTableViewCell.nib and b/Carthage/Build/iOS/LoopKit.framework/GlucoseRangeTableViewCell.nib differ diff --git a/Carthage/Build/iOS/LoopKit.framework/Info.plist b/Carthage/Build/iOS/LoopKit.framework/Info.plist index 7d2b1c3c86..b4a65224f0 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/Info.plist and b/Carthage/Build/iOS/LoopKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/LoopKit.framework/LoopKit b/Carthage/Build/iOS/LoopKit.framework/LoopKit index ef7ad31ed3..ef92f1586d 100755 Binary files a/Carthage/Build/iOS/LoopKit.framework/LoopKit and b/Carthage/Build/iOS/LoopKit.framework/LoopKit differ diff --git a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm.swiftmodule index 4cbbedc3e5..4a4c21de17 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm64.swiftmodule index 90cbb81c8f..eddd242b3e 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/i386.swiftmodule index 05646b748a..1515401b43 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/x86_64.swiftmodule index 793642bbee..e1e04d798a 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/LoopKit.framework/Modules/LoopKit.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/LoopKit.framework/RepeatingScheduleValueTableViewCell.nib b/Carthage/Build/iOS/LoopKit.framework/RepeatingScheduleValueTableViewCell.nib index 7c1d79915a..07bbbd149b 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/RepeatingScheduleValueTableViewCell.nib and b/Carthage/Build/iOS/LoopKit.framework/RepeatingScheduleValueTableViewCell.nib differ diff --git a/Carthage/Build/iOS/LoopKit.framework/TextFieldTableViewCell.nib b/Carthage/Build/iOS/LoopKit.framework/TextFieldTableViewCell.nib index 83aef0b927..459ca6de54 100644 Binary files a/Carthage/Build/iOS/LoopKit.framework/TextFieldTableViewCell.nib and b/Carthage/Build/iOS/LoopKit.framework/TextFieldTableViewCell.nib differ diff --git a/Carthage/Build/iOS/MinimedKit.framework/Info.plist b/Carthage/Build/iOS/MinimedKit.framework/Info.plist index 75b345dfc4..bb9aeeda03 100644 Binary files a/Carthage/Build/iOS/MinimedKit.framework/Info.plist and b/Carthage/Build/iOS/MinimedKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/MinimedKit.framework/MinimedKit b/Carthage/Build/iOS/MinimedKit.framework/MinimedKit index 3fbf4cf451..4a2e3d55aa 100755 Binary files a/Carthage/Build/iOS/MinimedKit.framework/MinimedKit and b/Carthage/Build/iOS/MinimedKit.framework/MinimedKit differ diff --git a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm.swiftmodule index 252917f3f6..d3fedcb028 100644 Binary files a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm64.swiftmodule index 3a110a1453..2bb9afc247 100644 Binary files a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/i386.swiftmodule index e781162f51..e31b65a9be 100644 Binary files a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/x86_64.swiftmodule index dd3d76fcf0..8073119e32 100644 Binary files a/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/MinimedKit.framework/Modules/MinimedKit.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/NightscoutUploadKit.framework/Info.plist b/Carthage/Build/iOS/NightscoutUploadKit.framework/Info.plist index 6e63e627bd..0dfc06fa6a 100644 Binary files a/Carthage/Build/iOS/NightscoutUploadKit.framework/Info.plist and b/Carthage/Build/iOS/NightscoutUploadKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/NightscoutUploadKit.framework/NightscoutUploadKit b/Carthage/Build/iOS/NightscoutUploadKit.framework/NightscoutUploadKit index 658c152b4a..44e074013c 100755 Binary files a/Carthage/Build/iOS/NightscoutUploadKit.framework/NightscoutUploadKit and b/Carthage/Build/iOS/NightscoutUploadKit.framework/NightscoutUploadKit differ diff --git a/Carthage/Build/iOS/RileyLinkBLEKit.framework/Info.plist b/Carthage/Build/iOS/RileyLinkBLEKit.framework/Info.plist index 6c547b4c3e..2b0bae0ff4 100644 Binary files a/Carthage/Build/iOS/RileyLinkBLEKit.framework/Info.plist and b/Carthage/Build/iOS/RileyLinkBLEKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/RileyLinkBLEKit.framework/RileyLinkBLEKit b/Carthage/Build/iOS/RileyLinkBLEKit.framework/RileyLinkBLEKit index 9e6bfbcdd1..31852f8bf0 100755 Binary files a/Carthage/Build/iOS/RileyLinkBLEKit.framework/RileyLinkBLEKit and b/Carthage/Build/iOS/RileyLinkBLEKit.framework/RileyLinkBLEKit differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/Info.plist b/Carthage/Build/iOS/RileyLinkKit.framework/Info.plist index 17f64a3324..079fc4b21f 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/Info.plist and b/Carthage/Build/iOS/RileyLinkKit.framework/Info.plist differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm.swiftmodule index b8884c17c4..4433eaad97 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm64.swiftmodule index 9918d42530..9337d5aefa 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/i386.swiftmodule index b386d6c601..6685d7fbdb 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/x86_64.swiftmodule index 6311c3fea0..b595496e69 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/RileyLinkKit.framework/Modules/RileyLinkKit.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkDeviceTableViewCell.nib b/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkDeviceTableViewCell.nib index b4b02983b5..0d1a4a97ca 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkDeviceTableViewCell.nib and b/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkDeviceTableViewCell.nib differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkKit b/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkKit index 0acb4dc2ae..fccb0ed451 100755 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkKit and b/Carthage/Build/iOS/RileyLinkKit.framework/RileyLinkKit differ diff --git a/Carthage/Build/iOS/RileyLinkKit.framework/TextFieldTableViewCell.nib b/Carthage/Build/iOS/RileyLinkKit.framework/TextFieldTableViewCell.nib index 8b4b8f79c0..62a57e4bc2 100644 Binary files a/Carthage/Build/iOS/RileyLinkKit.framework/TextFieldTableViewCell.nib and b/Carthage/Build/iOS/RileyLinkKit.framework/TextFieldTableViewCell.nib differ diff --git a/Carthage/Build/iOS/xDripG5.framework/Info.plist b/Carthage/Build/iOS/xDripG5.framework/Info.plist index 53ed8aad59..6e658e39d8 100644 Binary files a/Carthage/Build/iOS/xDripG5.framework/Info.plist and b/Carthage/Build/iOS/xDripG5.framework/Info.plist differ diff --git a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm.swiftmodule b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm.swiftmodule index 80cb2d6a24..36cbeddd66 100644 Binary files a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm.swiftmodule and b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm.swiftmodule differ diff --git a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm64.swiftmodule b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm64.swiftmodule index 328d4b9479..7ab99a6044 100644 Binary files a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm64.swiftmodule and b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/arm64.swiftmodule differ diff --git a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/i386.swiftmodule b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/i386.swiftmodule index 7a2526ed3f..7cc7528d0d 100644 Binary files a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/i386.swiftmodule and b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/i386.swiftmodule differ diff --git a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/x86_64.swiftmodule b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/x86_64.swiftmodule index ce914ded8e..29021619cb 100644 Binary files a/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/x86_64.swiftmodule and b/Carthage/Build/iOS/xDripG5.framework/Modules/xDripG5.swiftmodule/x86_64.swiftmodule differ diff --git a/Carthage/Build/iOS/xDripG5.framework/xDripG5 b/Carthage/Build/iOS/xDripG5.framework/xDripG5 index 0bad2351e4..091fb2c652 100755 Binary files a/Carthage/Build/iOS/xDripG5.framework/xDripG5 and b/Carthage/Build/iOS/xDripG5.framework/xDripG5 differ diff --git a/DoseMathTests/Info.plist b/DoseMathTests/Info.plist index 2556bb31c6..1b43d10edc 100644 --- a/DoseMathTests/Info.plist +++ b/DoseMathTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.13.0 + 1.0rc1 CFBundleSignature ???? CFBundleVersion diff --git a/LICENSE.md b/LICENSE.md index fe05606707..333c3ab974 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,6 +2,7 @@ With exceptions for frameworks and graphics, noted below. Copyright (c) 2015 Nathan Racklyeft +Copyright (c) 2016 LoopKit Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,6 +38,7 @@ Copyright (c) 2016 Mark Wilson *Including CarbKit.framework, GlucoseKit.framework, and InsulinKit.framework* Copyright (c) 2015 Nathan Racklyeft +Copyright (c) 2016 LoopKit Authors ## RileyLinkKit.framework *Including MinimedKit.framework, NightscoutUploadKit.framework, and RileyLinkBLEKit.framework* @@ -54,6 +56,7 @@ Copyright (c) 2016 Mark Wilson ## xDripG5.framework Copyright (c) 2015 Nathan Racklyeft +Copyright (c) 2016 LoopKit Authors > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index d82c739941..a98efa4269 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 4302F4DB1D4D6E9F00F0FCAF /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4302F4DA1D4D6E9F00F0FCAF /* NSData.swift */; }; 4302F4E11D4E9C8900F0FCAF /* TextFieldTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4302F4E01D4E9C8900F0FCAF /* TextFieldTableViewController.swift */; }; 4302F4E31D4EA54200F0FCAF /* InsulinDeliveryTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4302F4E21D4EA54200F0FCAF /* InsulinDeliveryTableViewController.swift */; }; 4302F4E51D4EA75100F0FCAF /* DoseStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4302F4E41D4EA75100F0FCAF /* DoseStore.swift */; }; @@ -16,7 +15,6 @@ 4313EDE01D8A6BF90060FA79 /* ChartContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4313EDDF1D8A6BF90060FA79 /* ChartContentView.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 */; }; - 4328E0181CFBE1DA00E199AA /* ContextInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0131CFBE1DA00E199AA /* ContextInterfaceController.swift */; }; 4328E01A1CFBE1DA00E199AA /* StatusInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0151CFBE1DA00E199AA /* StatusInterfaceController.swift */; }; 4328E01B1CFBE1DA00E199AA /* BolusInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E0161CFBE1DA00E199AA /* BolusInterfaceController.swift */; }; 4328E01E1CFBE25F00E199AA /* AddCarbsInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4328E01D1CFBE25F00E199AA /* AddCarbsInterfaceController.swift */; }; @@ -33,6 +31,8 @@ 4331E0781C85302200FBE832 /* CGPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4331E0771C85302200FBE832 /* CGPoint.swift */; }; 4331E07A1C85650D00FBE832 /* ChartAxisValueDoubleLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4331E0791C85650D00FBE832 /* ChartAxisValueDoubleLog.swift */; }; 4337615F1D52F487004A3647 /* GlucoseHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4337615E1D52F487004A3647 /* GlucoseHUDView.swift */; }; + 433EA4C21D9F39C900CD78FB /* PumpIDTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433EA4C11D9F39C900CD78FB /* PumpIDTableViewController.swift */; }; + 433EA4C41D9F71C800CD78FB /* CommandResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433EA4C31D9F71C800CD78FB /* CommandResponseViewController.swift */; }; 4346D1E71C77F5FE00ABAFE3 /* ChartTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4346D1E61C77F5FE00ABAFE3 /* ChartTableViewCell.swift */; }; 4346D1F01C781BEA00ABAFE3 /* SwiftCharts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */; }; 4346D1F61C78501000ABAFE3 /* ChartPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4346D1F51C78501000ABAFE3 /* ChartPoint.swift */; }; @@ -70,6 +70,9 @@ 437CEECA1CD84DB7003C8C80 /* BatteryLevelHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEC91CD84DB7003C8C80 /* BatteryLevelHUDView.swift */; }; 437CEEE41CDE5C0A003C8C80 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437CEEE31CDE5C0A003C8C80 /* UIImage.swift */; }; 437D9BA31D7BC977007245E8 /* PredictionTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437D9BA21D7BC977007245E8 /* PredictionTableViewController.swift */; }; + 43846AD51D8FA67800799272 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 43846AD41D8FA67800799272 /* Base.lproj */; }; + 43846AD91D8FA84B00799272 /* gallery.ckcomplication in Resources */ = {isa = PBXBuildFile; fileRef = 43846AD81D8FA84B00799272 /* gallery.ckcomplication */; }; + 43846ADB1D91057000799272 /* ContextUpdatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43846ADA1D91057000799272 /* ContextUpdatable.swift */; }; 43880F951D9CD54A009061A8 /* ChartPointsScatterDownTrianglesLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43880F941D9CD54A009061A8 /* ChartPointsScatterDownTrianglesLayer.swift */; }; 438849EA1D297CB6003B3F23 /* NightscoutService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438849E91D297CB6003B3F23 /* NightscoutService.swift */; }; 438849EC1D29EC34003B3F23 /* AmplitudeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438849EB1D29EC34003B3F23 /* AmplitudeService.swift */; }; @@ -96,11 +99,12 @@ 43C0944A1CACCC73001F6403 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C094491CACCC73001F6403 /* NotificationManager.swift */; }; 43C246A81D89990F0031F8D1 /* Crypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43C246A71D89990F0031F8D1 /* Crypto.framework */; }; 43C418B51CE0575200405B6A /* ShareGlucose+GlucoseKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C418B41CE0575200405B6A /* ShareGlucose+GlucoseKit.swift */; }; + 43C6407C1DA051850093E25D /* InsulinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43C6407B1DA051850093E25D /* InsulinKit.framework */; }; 43CA93371CB98079000026B5 /* MinimedKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43CA93361CB98079000026B5 /* MinimedKit.framework */; }; - 43CE7CDE1CA8B63E003CC1B0 /* NSDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CDD1CA8B63E003CC1B0 /* NSDate.swift */; }; + 43CB2B2B1D924D450079823D /* WCSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CB2B2A1D924D450079823D /* WCSession.swift */; }; + 43CE7CDE1CA8B63E003CC1B0 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CDD1CA8B63E003CC1B0 /* Data.swift */; }; 43DBF04C1C93B8D700B3C386 /* BolusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DBF04B1C93B8D700B3C386 /* BolusViewController.swift */; }; 43DBF0531C93EC8200B3C386 /* DeviceDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DBF0521C93EC8200B3C386 /* DeviceDataManager.swift */; }; - 43DBF0551C93ED3000B3C386 /* DeviceDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DBF0541C93ED3000B3C386 /* 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 */; }; @@ -124,9 +128,7 @@ 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 */; }; - 43E2D9161D222684004DA55F /* InsulinKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 43F78D4A1C914197002152D1 /* InsulinKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 43E2D9171D2226BD004DA55F /* LoopKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 43E2D9181D222759004DA55F /* InsulinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4A1C914197002152D1 /* InsulinKit.framework */; }; 43E2D9191D222759004DA55F /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; }; 43E3449F1B9D68E900C85C07 /* StatusTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E3449E1B9D68E900C85C07 /* StatusTableViewController.swift */; }; 43E344A41B9E1B1C00C85C07 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E344A31B9E1B1C00C85C07 /* NSUserDefaults.swift */; }; @@ -146,7 +148,6 @@ 43F78D261C8FC000002152D1 /* DoseMath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F78D251C8FC000002152D1 /* DoseMath.swift */; }; 43F78D4C1C914197002152D1 /* CarbKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D481C914197002152D1 /* CarbKit.framework */; }; 43F78D4D1C914197002152D1 /* GlucoseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D491C914197002152D1 /* GlucoseKit.framework */; }; - 43F78D4E1C914197002152D1 /* InsulinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4A1C914197002152D1 /* InsulinKit.framework */; }; 43F78D4F1C914197002152D1 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F78D4B1C914197002152D1 /* LoopKit.framework */; }; 43FBEDD81D73843700B21F22 /* LevelMaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FBEDD71D73843700B21F22 /* LevelMaskView.swift */; }; 4D3B40041D4A9E1A00BC6334 /* G4ShareSpy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */; }; @@ -230,14 +231,12 @@ dstSubfolderSpec = 10; files = ( 43E2D9171D2226BD004DA55F /* LoopKit.framework in CopyFiles */, - 43E2D9161D222684004DA55F /* InsulinKit.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 4302F4DA1D4D6E9F00F0FCAF /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; 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 = ""; }; 4302F4E41D4EA75100F0FCAF /* DoseStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoseStore.swift; sourceTree = ""; }; @@ -246,7 +245,6 @@ 4313EDDF1D8A6BF90060FA79 /* ChartContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartContentView.swift; sourceTree = ""; }; 4315D2861CA5CC3B00589052 /* CarbEntryEditTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryEditTableViewController.swift; sourceTree = ""; }; 4315D2891CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DiagnosticLogger+LoopKit.swift"; sourceTree = ""; }; - 4328E0131CFBE1DA00E199AA /* ContextInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextInterfaceController.swift; sourceTree = ""; }; 4328E0151CFBE1DA00E199AA /* StatusInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusInterfaceController.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 = ""; }; @@ -262,6 +260,8 @@ 4331E0771C85302200FBE832 /* CGPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGPoint.swift; sourceTree = ""; }; 4331E0791C85650D00FBE832 /* ChartAxisValueDoubleLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisValueDoubleLog.swift; sourceTree = ""; }; 4337615E1D52F487004A3647 /* GlucoseHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseHUDView.swift; sourceTree = ""; }; + 433EA4C11D9F39C900CD78FB /* PumpIDTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpIDTableViewController.swift; sourceTree = ""; }; + 433EA4C31D9F71C800CD78FB /* CommandResponseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandResponseViewController.swift; sourceTree = ""; }; 4346D1E61C77F5FE00ABAFE3 /* ChartTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartTableViewCell.swift; sourceTree = ""; }; 4346D1EF1C781BEA00ABAFE3 /* SwiftCharts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCharts.framework; path = Carthage/Build/iOS/SwiftCharts.framework; sourceTree = ""; }; 4346D1F51C78501000ABAFE3 /* ChartPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPoint.swift; sourceTree = ""; }; @@ -299,6 +299,9 @@ 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 = ""; }; + 43846AD41D8FA67800799272 /* Base.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base.lproj; sourceTree = ""; }; + 43846AD81D8FA84B00799272 /* gallery.ckcomplication */ = {isa = PBXFileReference; lastKnownFileType = folder; path = gallery.ckcomplication; sourceTree = ""; }; + 43846ADA1D91057000799272 /* ContextUpdatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextUpdatable.swift; sourceTree = ""; }; 43880F941D9CD54A009061A8 /* ChartPointsScatterDownTrianglesLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPointsScatterDownTrianglesLayer.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 = ""; }; @@ -327,12 +330,13 @@ 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 = ""; }; 43C418B41CE0575200405B6A /* ShareGlucose+GlucoseKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ShareGlucose+GlucoseKit.swift"; sourceTree = ""; }; + 43C6407B1DA051850093E25D /* InsulinKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InsulinKit.framework; path = Carthage/Build/iOS/InsulinKit.framework; sourceTree = ""; }; 43CA93361CB98079000026B5 /* MinimedKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MinimedKit.framework; path = Carthage/Build/iOS/MinimedKit.framework; sourceTree = ""; }; - 43CE7CDD1CA8B63E003CC1B0 /* NSDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDate.swift; sourceTree = ""; }; + 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 = ""; }; 43D533BB1CFD1DD7009E3085 /* WatchApp Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "WatchApp Extension.entitlements"; 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; }; - 43DBF0541C93ED3000B3C386 /* DeviceDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceDataManager.swift; sourceTree = ""; }; 43DBF0581C93F73800B3C386 /* CarbEntryTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryTableViewController.swift; sourceTree = ""; }; 43DE92501C541832001FFDE1 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 43DE92581C5479E4001FFDE1 /* CarbEntryUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryUserInfo.swift; sourceTree = ""; }; @@ -377,7 +381,6 @@ 43F78D251C8FC000002152D1 /* DoseMath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoseMath.swift; sourceTree = ""; }; 43F78D481C914197002152D1 /* CarbKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CarbKit.framework; path = Carthage/Build/iOS/CarbKit.framework; sourceTree = ""; }; 43F78D491C914197002152D1 /* GlucoseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GlucoseKit.framework; path = Carthage/Build/iOS/GlucoseKit.framework; sourceTree = ""; }; - 43F78D4A1C914197002152D1 /* InsulinKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InsulinKit.framework; path = Carthage/Build/iOS/InsulinKit.framework; sourceTree = ""; }; 43F78D4B1C914197002152D1 /* LoopKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKit.framework; path = Carthage/Build/iOS/LoopKit.framework; sourceTree = ""; }; 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 = ""; }; @@ -399,6 +402,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 43C6407C1DA051850093E25D /* InsulinKit.framework in Frameworks */, 438A95A81D8B9B24009D12E1 /* xDripG5.framework in Frameworks */, 43C246A81D89990F0031F8D1 /* Crypto.framework in Frameworks */, 434FB6461D68F1CD007B9C70 /* Amplitude.framework in Frameworks */, @@ -407,7 +411,6 @@ 4D3B40041D4A9E1A00BC6334 /* G4ShareSpy.framework in Frameworks */, 43F78D4D1C914197002152D1 /* GlucoseKit.framework in Frameworks */, 43F5C2C91B929C09003EB13D /* HealthKit.framework in Frameworks */, - 43F78D4E1C914197002152D1 /* InsulinKit.framework in Frameworks */, 43F78D4F1C914197002152D1 /* LoopKit.framework in Frameworks */, 43CA93371CB98079000026B5 /* MinimedKit.framework in Frameworks */, 43523EDB1CC35083001850F1 /* RileyLinkKit.framework in Frameworks */, @@ -427,7 +430,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 43E2D9181D222759004DA55F /* InsulinKit.framework in Frameworks */, 43E2D9191D222759004DA55F /* LoopKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -447,7 +449,7 @@ children = ( 4328E01D1CFBE25F00E199AA /* AddCarbsInterfaceController.swift */, 4328E0161CFBE1DA00E199AA /* BolusInterfaceController.swift */, - 4328E0131CFBE1DA00E199AA /* ContextInterfaceController.swift */, + 43846ADA1D91057000799272 /* ContextUpdatable.swift */, 43A943891B926B7B0051FA24 /* NotificationController.swift */, 4328E0151CFBE1DA00E199AA /* StatusInterfaceController.swift */, ); @@ -461,6 +463,7 @@ 4328E0201CFBE2C500E199AA /* IdentifiableClass.swift */, 4328E0231CFBE2C500E199AA /* NSUserDefaults.swift */, 4328E0241CFBE2C500E199AA /* UIColor.swift */, + 43CB2B2A1D924D450079823D /* WCSession.swift */, 4328E0251CFBE2C500E199AA /* WKAlertAction.swift */, 4328E02E1CFBF81800E199AA /* WKInterfaceImage.swift */, ); @@ -516,6 +519,7 @@ 43776F8E1B8022E90074EA36 /* Loop */ = { isa = PBXGroup; children = ( + 43846AD81D8FA84B00799272 /* gallery.ckcomplication */, 43EDEE6B1CF2E12A00393BE3 /* Loop.entitlements */, 43F5C2D41B92A4A6003EB13D /* Info.plist */, 43776F8F1B8022E90074EA36 /* AppDelegate.swift */, @@ -558,9 +562,9 @@ isa = PBXGroup; children = ( 43D533BB1CFD1DD7009E3085 /* WatchApp Extension.entitlements */, + 43846AD41D8FA67800799272 /* Base.lproj */, 43A943911B926B7B0051FA24 /* Info.plist */, 43A9438D1B926B7B0051FA24 /* ComplicationController.swift */, - 43DBF0541C93ED3000B3C386 /* DeviceDataManager.swift */, 43A943871B926B7B0051FA24 /* ExtensionDelegate.swift */, 43A9438F1B926B7B0051FA24 /* Assets.xcassets */, 4328E0121CFBE1B700E199AA /* Controllers */, @@ -630,8 +634,7 @@ 434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */, 434F54561D287FDB002A9274 /* NibLoadable.swift */, 430DA58D1D4AEC230097D1CA /* NSBundle.swift */, - 4302F4DA1D4D6E9F00F0FCAF /* NSData.swift */, - 43CE7CDD1CA8B63E003CC1B0 /* NSDate.swift */, + 43CE7CDD1CA8B63E003CC1B0 /* Data.swift */, 4398973A1CD2FC2000223065 /* NSDateFormatter.swift */, 436A0E7A1D7DE13400D6475D /* NSNumberFormatter.swift */, 439897341CD2F7DE00223065 /* NSTimeInterval.swift */, @@ -651,8 +654,10 @@ 43DBF04B1C93B8D700B3C386 /* BolusViewController.swift */, 4315D2861CA5CC3B00589052 /* CarbEntryEditTableViewController.swift */, 43DBF0581C93F73800B3C386 /* CarbEntryTableViewController.swift */, + 433EA4C31D9F71C800CD78FB /* CommandResponseViewController.swift */, 4302F4E21D4EA54200F0FCAF /* InsulinDeliveryTableViewController.swift */, 437D9BA21D7BC977007245E8 /* PredictionTableViewController.swift */, + 433EA4C11D9F39C900CD78FB /* PumpIDTableViewController.swift */, 43F5173C1D713DB0000FA422 /* RadioSelectionTableViewController.swift */, 43F5C2DA1B92A5E1003EB13D /* SettingsTableViewController.swift */, 43E3449E1B9D68E900C85C07 /* StatusTableViewController.swift */, @@ -721,6 +726,7 @@ 968DCD53F724DE56FFE51920 /* Frameworks */ = { isa = PBXGroup; children = ( + 43C6407B1DA051850093E25D /* InsulinKit.framework */, 438A95A71D8B9B24009D12E1 /* xDripG5.framework */, 434FB6451D68F1CD007B9C70 /* Amplitude.framework */, 43F78D481C914197002152D1 /* CarbKit.framework */, @@ -728,7 +734,6 @@ 4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */, 43F78D491C914197002152D1 /* GlucoseKit.framework */, 43F5C2C81B929C09003EB13D /* HealthKit.framework */, - 43F78D4A1C914197002152D1 /* InsulinKit.framework */, 43F78D4B1C914197002152D1 /* LoopKit.framework */, 43CA93361CB98079000026B5 /* MinimedKit.framework */, C10428961D17BAD400DD539A /* NightscoutUploadKit.framework */, @@ -844,7 +849,7 @@ attributes = { LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 0800; - ORGANIZATIONNAME = "Nathan Racklyeft"; + ORGANIZATIONNAME = "LoopKit Authors"; TargetAttributes = { 43776F8B1B8022E90074EA36 = { CreatedOnToolsVersion = 7.0; @@ -871,6 +876,9 @@ com.apple.ApplicationGroups.iOS = { enabled = 0; }; + com.apple.BackgroundModes.watchos.app = { + enabled = 0; + }; }; }; 43A9437D1B926B7B0051FA24 = { @@ -930,6 +938,7 @@ 43776F991B8022E90074EA36 /* Assets.xcassets in Resources */, 434F54591D28805E002A9274 /* ButtonTableViewCell.xib in Resources */, 43776F971B8022E90074EA36 /* Main.storyboard in Resources */, + 43846AD91D8FA84B00799272 /* gallery.ckcomplication in Resources */, 434F545B1D2880D4002A9274 /* AuthenticationTableViewCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -947,6 +956,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 43846AD51D8FA67800799272 /* Base.lproj in Resources */, 43A943901B926B7B0051FA24 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1019,7 +1029,7 @@ 430DA58E1D4AEC230097D1CA /* NSBundle.swift in Sources */, 43776F901B8022E90074EA36 /* AppDelegate.swift in Sources */, 437CCADA1D284ADF0075D2C3 /* AuthenticationTableViewCell.swift in Sources */, - 43CE7CDE1CA8B63E003CC1B0 /* NSDate.swift in Sources */, + 43CE7CDE1CA8B63E003CC1B0 /* Data.swift in Sources */, 43F41C331D3A17AA00C11ED6 /* ChartAxisValueDoubleUnit.swift in Sources */, 43F5C2DB1B92A5E1003EB13D /* SettingsTableViewController.swift in Sources */, 4313EDE01D8A6BF90060FA79 /* ChartContentView.swift in Sources */, @@ -1067,6 +1077,7 @@ 437D9BA31D7BC977007245E8 /* PredictionTableViewController.swift in Sources */, 43F41C371D3BF32400C11ED6 /* UIAlertController.swift in Sources */, 437CEEBC1CD6DE6A003C8C80 /* HUDView.swift in Sources */, + 433EA4C41D9F71C800CD78FB /* CommandResponseViewController.swift in Sources */, 436A0E7B1D7DE13400D6475D /* NSNumberFormatter.swift in Sources */, 434F545F1D288345002A9274 /* ShareService.swift in Sources */, 43FBEDD81D73843700B21F22 /* LevelMaskView.swift in Sources */, @@ -1076,8 +1087,8 @@ 436A0DA51D236A2A00104B24 /* LoopError.swift in Sources */, 437CEEC01CD6FCD8003C8C80 /* BasalRateHUDView.swift in Sources */, 43E2D8C61D204678004DA55F /* KeychainManager.swift in Sources */, - 4302F4DB1D4D6E9F00F0FCAF /* NSData.swift in Sources */, 437CEECA1CD84DB7003C8C80 /* BatteryLevelHUDView.swift in Sources */, + 433EA4C21D9F39C900CD78FB /* PumpIDTableViewController.swift in Sources */, 43F78D261C8FC000002152D1 /* DoseMath.swift in Sources */, 438D42F91D7C88BC003244B0 /* PredictionInputEffect.swift in Sources */, 4331E07A1C85650D00FBE832 /* ChartAxisValueDoubleLog.swift in Sources */, @@ -1105,8 +1116,6 @@ buildActionMask = 2147483647; files = ( 435400311C9F744E00D5819C /* BolusSuggestionUserInfo.swift in Sources */, - 43DBF0551C93ED3000B3C386 /* DeviceDataManager.swift in Sources */, - 4328E0181CFBE1DA00E199AA /* ContextInterfaceController.swift in Sources */, 43A9438A1B926B7B0051FA24 /* NotificationController.swift in Sources */, 43A943881B926B7B0051FA24 /* ExtensionDelegate.swift in Sources */, 4328E0291CFBE2C500E199AA /* NSUserDefaults.swift in Sources */, @@ -1116,8 +1125,10 @@ 4328E02B1CFBE2C500E199AA /* WKAlertAction.swift in Sources */, 4328E0281CFBE2C500E199AA /* CLKComplicationTemplate.swift in Sources */, 4328E01E1CFBE25F00E199AA /* AddCarbsInterfaceController.swift in Sources */, + 43846ADB1D91057000799272 /* ContextUpdatable.swift in Sources */, 43EA285F1D50ED3D001BC233 /* GlucoseTrend.swift in Sources */, 4328E0261CFBE2C500E199AA /* IdentifiableClass.swift in Sources */, + 43CB2B2B1D924D450079823D /* WCSession.swift in Sources */, 43DE925A1C5479E4001FFDE1 /* CarbEntryUserInfo.swift in Sources */, 4328E0301CFBFAEB00E199AA /* NSTimeInterval.swift in Sources */, 43A9438E1B926B7B0051FA24 /* ComplicationController.swift in Sources */, @@ -1215,7 +1226,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer: loudnate@gmail.com (XZN842LDLT)"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1233,7 +1244,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MAIN_APP_BUNDLE_IDENTIFIER = "$(inherited).Loop"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1267,7 +1278,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer: loudnate@gmail.com (XZN842LDLT)"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1279,7 +1290,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MAIN_APP_BUNDLE_IDENTIFIER = "$(inherited).Loop"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; diff --git a/Loop/AppDelegate.swift b/Loop/AppDelegate.swift index c0610119a1..f2f0868f3c 100644 --- a/Loop/AppDelegate.swift +++ b/Loop/AppDelegate.swift @@ -7,6 +7,7 @@ // import UIKit +import UserNotifications import CarbKit import InsulinKit @@ -20,7 +21,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { window?.tintColor = UIColor.tintColor - NotificationManager.authorize() + NotificationManager.authorize(delegate: self) AnalyticsManager.sharedManager.application(application, didFinishLaunchingWithOptions: launchOptions) @@ -60,27 +61,25 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } - // MARK: - Notifications + // MARK: - 3D Touch - func application(_ application: UIApplication, didReceive notification: UILocalNotification) { - if application.applicationState == .active { - if let message = notification.alertBody { - window?.rootViewController?.presentAlertController(withTitle: notification.alertTitle, message: message, animated: true, completion: nil) - } - } + func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { + completionHandler(false) } +} - func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable: Any], completionHandler: @escaping () -> Void) { - switch identifier { - case NotificationManager.Action.RetryBolus.rawValue?: - if let units = notification.userInfo?[NotificationManager.UserInfoKey.BolusAmount.rawValue] as? Double, - let startDate = notification.userInfo?[NotificationManager.UserInfoKey.BolusStartDate.rawValue] as? Date, +extension AppDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + switch response.actionIdentifier { + case NotificationManager.Action.RetryBolus.rawValue: + if let units = response.notification.request.content.userInfo[NotificationManager.UserInfoKey.BolusAmount.rawValue] as? Double, + let startDate = response.notification.request.content.userInfo[NotificationManager.UserInfoKey.BolusStartDate.rawValue] as? Date, startDate.timeIntervalSinceNow >= TimeInterval(minutes: -5) { AnalyticsManager.sharedManager.didRetryBolus() - dataManager.enactBolus(units) { (error) in + dataManager.enactBolus(units: units) { (error) in if error != nil { NotificationManager.sendBolusFailureNotificationForAmount(units, atStartDate: startDate) } @@ -92,13 +91,11 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { default: break } - + completionHandler() } - // MARK: - 3D Touch - - func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { - completionHandler(false) + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + completionHandler([.badge, .sound, .alert]) } } diff --git a/Loop/Base.lproj/Main.storyboard b/Loop/Base.lproj/Main.storyboard index 58ad1c7ec0..7389695acf 100644 --- a/Loop/Base.lproj/Main.storyboard +++ b/Loop/Base.lproj/Main.storyboard @@ -32,9 +32,9 @@ - + - + diff --git a/Loop/Extensions/NSDate.swift b/Loop/Extensions/Data.swift similarity index 100% rename from Loop/Extensions/NSDate.swift rename to Loop/Extensions/Data.swift diff --git a/Loop/Extensions/NSData.swift b/Loop/Extensions/NSData.swift deleted file mode 100644 index 7ce1922d72..0000000000 --- a/Loop/Extensions/NSData.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// NSData.swift -// Loop -// -// Created by Nate Racklyeft on 7/30/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import Foundation - -/* -extension Data { - @nonobjc subscript(index: Int) -> UInt8 { - let bytes: [UInt8] = self[index...index] - - return bytes[0] - } - - subscript(range: Range) -> [UInt8] { - var dataArray = [UInt8](repeating: 0, count: range.count) - (self as NSData).getBytes(&dataArray, range: NSRange(range)) - - return dataArray - } - - subscript(range: Range) -> Data { - return subdata(in: NSRange(range)) - } -} -*/ diff --git a/Loop/Extensions/NSUserDefaults.swift b/Loop/Extensions/NSUserDefaults.swift index 1b67795d96..5d924a630c 100644 --- a/Loop/Extensions/NSUserDefaults.swift +++ b/Loop/Extensions/NSUserDefaults.swift @@ -8,6 +8,7 @@ import Foundation import LoopKit +import MinimedKit extension UserDefaults { @@ -27,6 +28,7 @@ extension UserDefaults { case PreferredInsulinDataSource = "com.loudnate.Loop.PreferredInsulinDataSource" case PumpID = "com.loudnate.Naterade.PumpID" case PumpModelNumber = "com.loudnate.Naterade.PumpModelNumber" + case PumpRegion = "com.loopkit.Loop.PumpRegion" case PumpTimeZone = "com.loudnate.Naterade.PumpTimeZone" case RetrospectiveCorrectionEnabled = "com.loudnate.Loop.RetrospectiveCorrectionEnabled" } @@ -177,6 +179,16 @@ extension UserDefaults { } } + var pumpRegion: PumpRegion? { + get { + // Defaults to 0 / northAmerica + return PumpRegion(rawValue: integer(forKey: Key.PumpRegion.rawValue)) + } + set { + set(newValue?.rawValue, forKey: Key.PumpRegion.rawValue) + } + } + var pumpTimeZone: TimeZone? { get { if let offset = object(forKey: Key.PumpTimeZone.rawValue) as? NSNumber { diff --git a/Loop/Info.plist b/Loop/Info.plist index 816f483473..382a20f5e8 100644 --- a/Loop/Info.plist +++ b/Loop/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.13.0 + 1.0rc1 CFBundleSignature ???? CFBundleVersion diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift index 673bb7496a..dbaa66f920 100644 --- a/Loop/Managers/DeviceDataManager.swift +++ b/Loop/Managers/DeviceDataManager.swift @@ -114,7 +114,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter AnalyticsManager.sharedManager.didChangeRileyLinkConnectionState() if connectedPeripheralIDs.count == 0 { - NotificationManager.clearLoopNotRunningNotifications() + NotificationManager.clearPendingNotificationRequests() } } @@ -360,13 +360,12 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter } } - /** - Send a bolus command and handle the result - - - parameter completion: A closure called after the command is complete. This closure takes a single argument: - - error: An error describing why the command failed - */ - func enactBolus(_ units: Double, completion: @escaping (_ error: Error?) -> Void) { + /// Send a bolus command and handle the result + /// + /// - parameter units: The number of units to deliver + /// - parameter completion: A clsure called after the command is complete. This closure takes a single argument: + /// - error: An error describing why the command failed + func enactBolus(units: Double, completion: @escaping (_ error: Error?) -> Void) { guard units > 0 else { completion(nil) return @@ -442,9 +441,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter } // MARK: - G5 Transmitter - /** - The G5 transmitter is a reliable heartbeat by which we can assert the loop state. - */ + /// The G5 transmitter is a reliable heartbeat by which we can assert the loop state. // MARK: TransmitterDelegate @@ -485,11 +482,19 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter } } + public func transmitter(_ transmitter: Transmitter, didReadUnknownData data: Data) { + logger.addMessage([ + "unknownData": data.hexadecimalString, + "collectedAt": DateFormatter.ISO8601StrictDateFormatter().string(from: Date()) + ], toCollection: "g5" + ) + } + // MARK: G5 data - private var latestGlucoseG5: xDripG5.Glucose? + fileprivate var latestGlucoseG5: xDripG5.Glucose? - private var latestGlucoseFromShare: ShareGlucose? + fileprivate var latestGlucoseFromShare: ShareGlucose? /** Attempts to backfill glucose data from the share servers if a G5 connection hasn't been established. @@ -543,7 +548,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter // MARK: ReceiverDelegate - private var latestGlucoseG4: GlucoseG4? + fileprivate var latestGlucoseG4: GlucoseG4? func receiver(_ receiver: Receiver, didReadGlucoseHistory glucoseHistory: [GlucoseG4]) { assertCurrentPumpData() @@ -613,7 +618,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter var pumpID = newValue if let pumpID = pumpID, pumpID.characters.count == 6 { - let pumpState = PumpState(pumpID: pumpID, pumpRegion: .northAmerica) + let pumpState = PumpState(pumpID: pumpID, pumpRegion: self.pumpState?.pumpRegion ?? .northAmerica) if let timeZone = self.pumpState?.timeZone { pumpState.timeZone = timeZone @@ -674,6 +679,8 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter } UserDefaults.standard.pumpModelNumber = pumpState?.pumpModel?.rawValue + case "pumpRegion"?: + UserDefaults.standard.pumpRegion = pumpState?.pumpRegion case "lastHistoryDump"?, "awakeUntil"?: break default: @@ -884,6 +891,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter ) carbStore = CarbStore( + defaultAbsorptionTimes: (fast: TimeInterval(hours: 2), medium: TimeInterval(hours: 3), slow: TimeInterval(hours: 4)), carbRatioSchedule: carbRatioSchedule, insulinSensitivitySchedule: insulinSensitivitySchedule ) @@ -891,7 +899,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter var idleListeningEnabled = true if let pumpID = pumpID { - let pumpState = PumpState(pumpID: pumpID, pumpRegion: .northAmerica) + let pumpState = PumpState(pumpID: pumpID, pumpRegion: UserDefaults.standard.pumpRegion ?? .northAmerica) if let timeZone = UserDefaults.standard.pumpTimeZone { pumpState.timeZone = timeZone @@ -944,6 +952,28 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter } +extension DeviceDataManager: CustomDebugStringConvertible { + var debugDescription: String { + return [ + "## DeviceDataManager", + "receiverEnabled: \(receiverEnabled)", + "latestPumpStatusFromMySentry: \(latestPumpStatusFromMySentry)", + "latestGlucoseG5: \(latestGlucoseG5)", + "latestGlucoseFromShare: \(latestGlucoseFromShare)", + "latestGlucoseG4: \(latestGlucoseG4)", + "pumpState: \(String(reflecting: pumpState))", + "preferredInsulinDataSource: \(preferredInsulinDataSource)", + "transmitterID: \(transmitterID)", + "glucoseTargetRangeSchedule: \(glucoseTargetRangeSchedule?.debugDescription ?? "")", + "workoutModeEnabled: \(workoutModeEnabled)", + "maximumBasalRatePerHour: \(maximumBasalRatePerHour)", + "maximumBolus: \(maximumBolus)", + String(reflecting: rileyLinkManager) + ].joined(separator: "\n") + } +} + + extension Notification.Name { /// Notification posted by the instance when new glucose data was processed static let GlucoseUpdated = Notification.Name(rawValue: "com.loudnate.Naterade.notification.GlucoseUpdated") diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index 56c2491ff5..f5edaaa111 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -666,6 +666,30 @@ final class LoopDataManager { } +extension LoopDataManager { + /// Generates a diagnostic report about the current state + /// + /// This operation is performed asynchronously and the completion will be executed on an arbitrary background queue. + /// + /// - parameter completionHandler: A closure called once the report has been generated. The closure takes a single argument of the report string. + func generateDiagnosticReport(_ completionHandler: @escaping (_ report: String) -> Void) { + getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, recommendedTempBasal, lastTempBasal, lastLoopCompleted, insulinOnBoard, error) in + let report = [ + "## LoopDataManager", + "predictedGlucose: \(predictedGlucose ?? [])", + "retrospectivePredictedGlucose: \(retrospectivePredictedGlucose ?? [])", + "recommendedTempBasal: \(recommendedTempBasal)", + "lastTempBasal: \(lastTempBasal)", + "lastLoopCompleted: \(lastLoopCompleted ?? .distantPast)", + "insulinOnBoard: \(insulinOnBoard)", + "error: \(error)" + ] + completionHandler(report.joined(separator: "\n")) + } + } +} + + extension Notification.Name { static let LoopDataUpdated = Notification.Name(rawValue: "com.loudnate.Naterade.notification.LoopDataUpdated") diff --git a/Loop/Managers/NotificationManager.swift b/Loop/Managers/NotificationManager.swift index 47c729385c..6667183cd7 100644 --- a/Loop/Managers/NotificationManager.swift +++ b/Loop/Managers/NotificationManager.swift @@ -7,6 +7,7 @@ // import UIKit +import UserNotifications struct NotificationManager { @@ -27,43 +28,44 @@ struct NotificationManager { case BolusStartDate } - static var userNotificationSettings: UIUserNotificationSettings { - let retryBolusAction = UIMutableUserNotificationAction() - retryBolusAction.title = NSLocalizedString("Retry", comment: "The title of the notification action to retry a bolus command") - retryBolusAction.identifier = Action.RetryBolus.rawValue - retryBolusAction.activationMode = .background - - let bolusFailureCategory = UIMutableUserNotificationCategory() - bolusFailureCategory.identifier = Category.BolusFailure.rawValue - bolusFailureCategory.setActions([ - retryBolusAction - ], - for: .default - ) + private static var notificationCategories: Set { + var categories = [UNNotificationCategory]() - return UIUserNotificationSettings( - types: [.badge, .sound, .alert], - categories: [ - bolusFailureCategory - ] + let retryBolusAction = UNNotificationAction( + identifier: Action.RetryBolus.rawValue, + title: NSLocalizedString("Retry", comment: "The title of the notification action to retry a bolus command"), + options: [] ) + + categories.append(UNNotificationCategory( + identifier: Category.BolusFailure.rawValue, + actions: [retryBolusAction], + intentIdentifiers: [], + options: [] + )) + + return Set(categories) } - static func authorize() { - UIApplication.shared.registerUserNotificationSettings(userNotificationSettings) + static func authorize(delegate: UNUserNotificationCenterDelegate) { + let center = UNUserNotificationCenter.current() + + center.delegate = delegate + center.requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { _, _ in }) + center.setNotificationCategories(notificationCategories) } // MARK: - Notifications static func sendBolusFailureNotificationForAmount(_ units: Double, atStartDate startDate: Date) { - let notification = UILocalNotification() + let notification = UNMutableNotificationContent() - notification.alertTitle = NSLocalizedString("Bolus", comment: "The notification title for a bolus failure") - notification.alertBody = String(format: NSLocalizedString("%@ U bolus may have failed.", comment: "The notification alert describing a possible bolus failure. The substitution parameter is the size of the bolus in units."), NumberFormatter.localizedString(from: NSNumber(value: units), number: .decimal)) - notification.soundName = UILocalNotificationDefaultSoundName + notification.title = NSLocalizedString("Bolus", comment: "The notification title for a bolus failure") + notification.body = String(format: NSLocalizedString("%@ U bolus may have failed.", comment: "The notification alert describing a possible bolus failure. The substitution parameter is the size of the bolus in units."), NumberFormatter.localizedString(from: NSNumber(value: units), number: .decimal)) + notification.sound = UNNotificationSound.default() if startDate.timeIntervalSinceNow >= TimeInterval(minutes: -5) { - notification.category = Category.BolusFailure.rawValue + notification.categoryIdentifier = Category.BolusFailure.rawValue } notification.userInfo = [ @@ -71,30 +73,27 @@ struct NotificationManager { UserInfoKey.BolusStartDate.rawValue: startDate ] - UIApplication.shared.presentLocalNotificationNow(notification) + let request = UNNotificationRequest( + // Only support 1 bolus notification at once + identifier: Category.BolusFailure.rawValue, + content: notification, + trigger: nil + ) + + UNUserNotificationCenter.current().add(request) } // Cancel any previous scheduled notifications in the Loop Not Running category - static func clearLoopNotRunningNotifications() { - let app = UIApplication.shared - - app.scheduledLocalNotifications?.filter({ - $0.category == Category.LoopNotRunning.rawValue - }).forEach({ - app.cancelLocalNotification($0) - }) + static func clearPendingNotificationRequests() { + UNUserNotificationCenter.current().removeAllPendingNotificationRequests() } static func scheduleLoopNotRunningNotifications() { - let app = UIApplication.shared - - clearLoopNotRunningNotifications() - // Give a little extra time for a loop-in-progress to complete let gracePeriod = TimeInterval(minutes: 0.5) for minutes: Double in [20, 40, 60, 120] { - let notification = UILocalNotification() + let notification = UNMutableNotificationContent() let failureInterval = TimeInterval(minutes: minutes) let formatter = DateComponentsFormatter() @@ -103,46 +102,66 @@ struct NotificationManager { formatter.unitsStyle = .full if let failueIntervalString = formatter.string(from: failureInterval)?.localizedLowercase { - notification.alertBody = String(format: NSLocalizedString("Loop has not completed successfully in %@", comment: "The notification alert describing a long-lasting loop failure. The substitution parameter is the time interval since the last loop"), failueIntervalString) + notification.body = String(format: NSLocalizedString("Loop has not completed successfully in %@", comment: "The notification alert describing a long-lasting loop failure. The substitution parameter is the time interval since the last loop"), failueIntervalString) } - notification.alertTitle = NSLocalizedString("Loop Failure", comment: "The notification title for a loop failure") - notification.fireDate = Date(timeIntervalSinceNow: failureInterval + gracePeriod) - notification.soundName = UILocalNotificationDefaultSoundName - notification.category = Category.LoopNotRunning.rawValue - - app.scheduleLocalNotification(notification) + notification.title = NSLocalizedString("Loop Failure", comment: "The notification title for a loop failure") + notification.sound = UNNotificationSound.default() + notification.categoryIdentifier = Category.LoopNotRunning.rawValue + notification.threadIdentifier = Category.LoopNotRunning.rawValue + + let request = UNNotificationRequest( + identifier: "\(Category.LoopNotRunning.rawValue)\(failureInterval)", + content: notification, + trigger: UNTimeIntervalNotificationTrigger( + timeInterval: failureInterval + gracePeriod, + repeats: false + ) + ) + + UNUserNotificationCenter.current().add(request) } } static func sendPumpBatteryLowNotification() { - let notification = UILocalNotification() + let notification = UNMutableNotificationContent() + + notification.title = NSLocalizedString("Pump Battery Low", comment: "The notification title for a low pump battery") + notification.body = NSLocalizedString("Change the pump battery immediately", comment: "The notification alert describing a low pump battery") + notification.sound = UNNotificationSound.default() + notification.categoryIdentifier = Category.PumpBatteryLow.rawValue - notification.alertTitle = NSLocalizedString("Pump Battery Low", comment: "The notification title for a low pump battery") - notification.alertBody = NSLocalizedString("Change the pump battery immediately", comment: "The notification alert describing a low pump battery") - notification.soundName = UILocalNotificationDefaultSoundName - notification.category = Category.PumpBatteryLow.rawValue + let request = UNNotificationRequest( + identifier: Category.PumpBatteryLow.rawValue, + content: notification, + trigger: nil + ) - UIApplication.shared.presentLocalNotificationNow(notification) + UNUserNotificationCenter.current().add(request) } static func sendPumpReservoirEmptyNotification() { - let notification = UILocalNotification() - - notification.alertTitle = NSLocalizedString("Pump Reservoir Empty", comment: "The notification title for an empty pump reservoir") - notification.alertBody = NSLocalizedString("Change the pump reservoir now", comment: "The notification alert describing an empty pump reservoir") - notification.soundName = UILocalNotificationDefaultSoundName - notification.category = Category.PumpReservoirEmpty.rawValue - - // TODO: Add an action to Suspend the pump + let notification = UNMutableNotificationContent() + + notification.title = NSLocalizedString("Pump Reservoir Empty", comment: "The notification title for an empty pump reservoir") + notification.body = NSLocalizedString("Change the pump reservoir now", comment: "The notification alert describing an empty pump reservoir") + notification.sound = UNNotificationSound.default() + notification.categoryIdentifier = Category.PumpReservoirEmpty.rawValue + + let request = UNNotificationRequest( + // Not a typo: this should replace any pump reservoir low notifications + identifier: Category.PumpReservoirLow.rawValue, + content: notification, + trigger: nil + ) - UIApplication.shared.presentLocalNotificationNow(notification) + UNUserNotificationCenter.current().add(request) } static func sendPumpReservoirLowNotificationForAmount(_ units: Double, andTimeRemaining remaining: TimeInterval?) { - let notification = UILocalNotification() + let notification = UNMutableNotificationContent() - notification.alertTitle = NSLocalizedString("Pump Reservoir Low", comment: "The notification title for a low pump reservoir") + notification.title = NSLocalizedString("Pump Reservoir Low", comment: "The notification title for a low pump reservoir") let unitsString = NumberFormatter.localizedString(from: NSNumber(value: units), number: .decimal) @@ -154,14 +173,20 @@ struct NotificationManager { intervalFormatter.includesTimeRemainingPhrase = true if let remaining = remaining, let timeString = intervalFormatter.string(from: remaining) { - notification.alertBody = String(format: NSLocalizedString("%1$@ U left: %2$@", comment: "Low reservoir alert with time remaining format string. (1: Number of units remaining)(2: approximate time remaining)"), unitsString, timeString) + notification.body = String(format: NSLocalizedString("%1$@ U left: %2$@", comment: "Low reservoir alert with time remaining format string. (1: Number of units remaining)(2: approximate time remaining)"), unitsString, timeString) } else { - notification.alertBody = String(format: NSLocalizedString("%1$@ U left", comment: "Low reservoir alert format string. (1: Number of units remaining)"), unitsString) + notification.body = String(format: NSLocalizedString("%1$@ U left", comment: "Low reservoir alert format string. (1: Number of units remaining)"), unitsString) } - notification.soundName = UILocalNotificationDefaultSoundName - notification.category = Category.PumpReservoirLow.rawValue + notification.sound = UNNotificationSound.default() + notification.categoryIdentifier = Category.PumpReservoirLow.rawValue + + let request = UNNotificationRequest( + identifier: Category.PumpReservoirLow.rawValue, + content: notification, + trigger: nil + ) - UIApplication.shared.presentLocalNotificationNow(notification) + UNUserNotificationCenter.current().add(request) } } diff --git a/Loop/Managers/StatusChartManager.swift b/Loop/Managers/StatusChartManager.swift index 7779751e66..ed942f7bed 100644 --- a/Loop/Managers/StatusChartManager.swift +++ b/Loop/Managers/StatusChartManager.swift @@ -74,7 +74,15 @@ final class StatusChartsManager { var startDate = Date() - var glucoseUnit: HKUnit = HKUnit.milligramsPerDeciliterUnit() + var glucoseUnit: HKUnit = HKUnit.milligramsPerDeciliterUnit() { + didSet { + if glucoseUnit != oldValue { + // Regenerate the glucose display points + let oldRange = glucoseDisplayRange + glucoseDisplayRange = oldRange + } + } + } var glucoseTargetRangeSchedule: GlucoseRangeSchedule? diff --git a/Loop/Managers/WatchDataManager.swift b/Loop/Managers/WatchDataManager.swift index 7ab5d36f82..14670c1364 100644 --- a/Loop/Managers/WatchDataManager.swift +++ b/Loop/Managers/WatchDataManager.swift @@ -160,7 +160,7 @@ final class WatchDataManager: NSObject, WCSessionDelegate { } case SetBolusUserInfo.name?: if let bolus = SetBolusUserInfo(rawValue: message as SetBolusUserInfo.RawValue) { - self.deviceDataManager.enactBolus(bolus.value) { (error) in + self.deviceDataManager.enactBolus(units: bolus.value) { (error) in if error != nil { NotificationManager.sendBolusFailureNotificationForAmount(bolus.value, atStartDate: bolus.startDate) } else { diff --git a/Loop/Models/Glucose.swift b/Loop/Models/Glucose.swift index 4907322953..0ae2ca0cb1 100644 --- a/Loop/Models/Glucose.swift +++ b/Loop/Models/Glucose.swift @@ -51,4 +51,8 @@ extension Glucose: SensorDisplayable { return .upUpUp } } + + var isLocal: Bool { + return true + } } diff --git a/Loop/Models/GlucoseG4.swift b/Loop/Models/GlucoseG4.swift index da4603c138..30df44498d 100644 --- a/Loop/Models/GlucoseG4.swift +++ b/Loop/Models/GlucoseG4.swift @@ -39,4 +39,8 @@ extension GlucoseG4: SensorDisplayable { var trendType: GlucoseTrend? { return GlucoseTrend(rawValue: Int(trend)) } + + var isLocal: Bool { + return true + } } diff --git a/Loop/Models/MySentryPumpStatusMessageBody.swift b/Loop/Models/MySentryPumpStatusMessageBody.swift index f2ce8a911d..253a0423e7 100644 --- a/Loop/Models/MySentryPumpStatusMessageBody.swift +++ b/Loop/Models/MySentryPumpStatusMessageBody.swift @@ -38,4 +38,8 @@ extension MySentryPumpStatusMessageBody: SensorDisplayable { return .flat } } + + var isLocal: Bool { + return true + } } diff --git a/Loop/Models/PredictionInputEffect.swift b/Loop/Models/PredictionInputEffect.swift index 779f63b645..1b257e3d51 100644 --- a/Loop/Models/PredictionInputEffect.swift +++ b/Loop/Models/PredictionInputEffect.swift @@ -38,7 +38,7 @@ enum PredictionInputEffect { case .momentum: return NSLocalizedString("15 min glucose regression coefficient (b₁), continued with decay over 30 min", comment: "Description of the prediction input effect for glucose momentum") case .retrospection: - return NSLocalizedString("30 mim comparison of glucose prediction vs actual, continued with decay over 60 min", comment: "Description of the prediction input effect for retrospective correction") + return NSLocalizedString("30 min comparison of glucose prediction vs actual, continued with decay over 60 min", comment: "Description of the prediction input effect for retrospective correction") } } } diff --git a/Loop/Models/SensorDisplayable.swift b/Loop/Models/SensorDisplayable.swift index 7cf7723110..f71838e3dd 100644 --- a/Loop/Models/SensorDisplayable.swift +++ b/Loop/Models/SensorDisplayable.swift @@ -18,6 +18,9 @@ protocol SensorDisplayable { /// Enumerates the trend of the sensor values var trendType: GlucoseTrend? { get } + + /// Returns wheter the data is from a locally-connected device + var isLocal: Bool { get } } diff --git a/Loop/Models/ShareGlucose+GlucoseKit.swift b/Loop/Models/ShareGlucose+GlucoseKit.swift index 0d1ab5746d..7aad5740e9 100644 --- a/Loop/Models/ShareGlucose+GlucoseKit.swift +++ b/Loop/Models/ShareGlucose+GlucoseKit.swift @@ -31,4 +31,8 @@ extension ShareGlucose: SensorDisplayable { var trendType: GlucoseTrend? { return GlucoseTrend(rawValue: Int(trend)) } + + var isLocal: Bool { + return false + } } diff --git a/Loop/View Controllers/CommandResponseViewController.swift b/Loop/View Controllers/CommandResponseViewController.swift new file mode 100644 index 0000000000..7ad0fdedca --- /dev/null +++ b/Loop/View Controllers/CommandResponseViewController.swift @@ -0,0 +1,68 @@ +// +// CommandResponseViewController.swift +// Loop +// +// Created by Nate Racklyeft on 9/30/16. +// Copyright © 2016 LoopKit Authors. All rights reserved. +// + +import Foundation +import LoopKit + + +extension CommandResponseViewController { + static func generateDiagnosticReport(dataManager: DeviceDataManager) -> CommandResponseViewController { + let vc = CommandResponseViewController(command: { (completionHandler) in + let group = DispatchGroup() + + var doseStoreResponse = "" + group.enter() + dataManager.doseStore.generateDiagnosticReport { (report) in + doseStoreResponse = report + group.leave() + } + + var carbStoreResponse = "" + if let carbStore = dataManager.carbStore { + group.enter() + carbStore.generateDiagnosticReport { (report) in + carbStoreResponse = report + group.leave() + } + } + + var glucoseStoreResponse = "" + if let glucoseStore = dataManager.glucoseStore { + group.enter() + glucoseStore.generateDiagnosticReport { (report) in + glucoseStoreResponse = report + group.leave() + } + } + + // LoopStatus + var loopManagerResponse = "" + group.enter() + dataManager.loopManager.generateDiagnosticReport { (report) in + loopManagerResponse = report + group.leave() + } + + group.notify(queue: DispatchQueue.main) { + completionHandler([ + "Use the Share button above save this diagnostic report to aid investigating your problem. Issues can be filed at https://github.com/LoopKit/Loop/issues.", + "Generated: \(Date())", + String(reflecting: dataManager), + loopManagerResponse, + doseStoreResponse, + carbStoreResponse, + glucoseStoreResponse + ].joined(separator: "\n\n")) + } + + return NSLocalizedString("Loading...", comment: "The loading message for the diagnostic report screen") + }) + + return vc + } +} diff --git a/Loop/View Controllers/PredictionTableViewController.swift b/Loop/View Controllers/PredictionTableViewController.swift index a5a0568d3d..ba16d503dd 100644 --- a/Loop/View Controllers/PredictionTableViewController.swift +++ b/Loop/View Controllers/PredictionTableViewController.swift @@ -300,7 +300,7 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass, U if let eventualGlucose = eventualGlucoseDescription { cell.titleLabel?.text = String(format: NSLocalizedString("Eventually %@", comment: "The subtitle format describing eventual glucose. (1: localized glucose value description)"), eventualGlucose) } else { - cell.titleLabel?.text = nil + cell.titleLabel?.text = "–" } default: break @@ -321,7 +321,7 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass, U override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { switch Section(rawValue: indexPath.section)! { case .charts: - return 270 + return 275 case .inputs, .settings: return 60 } diff --git a/Loop/View Controllers/PumpIDTableViewController.swift b/Loop/View Controllers/PumpIDTableViewController.swift new file mode 100644 index 0000000000..8b5fd13851 --- /dev/null +++ b/Loop/View Controllers/PumpIDTableViewController.swift @@ -0,0 +1,113 @@ +// +// PumpIDTableViewController.swift +// Loop +// +// Created by Nate Racklyeft on 9/30/16. +// Copyright © 2016 LoopKit Authors. All rights reserved. +// + +import UIKit +import MinimedKit +import LoopKit + +protocol PumpIDTableViewControllerDelegate: TextFieldTableViewControllerDelegate { + func pumpIDTableViewControllerDidChangePumpRegion(_ controller: PumpIDTableViewController) +} + + +extension PumpRegion { + static let count = 2 +} + + +final class PumpIDTableViewController: TextFieldTableViewController { + + /// The selected pump region + var region: PumpRegion? { + didSet { + if let oldValue = oldValue, oldValue != region { + tableView.cellForRow(at: IndexPath(row: oldValue.rawValue, section: Section.region.rawValue))?.accessoryType = .none + } + + if let region = region, oldValue != region { + tableView.cellForRow(at: IndexPath(row: region.rawValue, section: Section.region.rawValue))?.accessoryType = .checkmark + } + + if let delegate = delegate as? PumpIDTableViewControllerDelegate { + delegate.pumpIDTableViewControllerDidChangePumpRegion(self) + } + } + } + + convenience init(pumpID: String?, region: PumpRegion?) { + self.init(style: .grouped) + + self.region = region + + placeholder = NSLocalizedString("Enter the 6-digit pump ID", comment: "The placeholder text instructing users how to enter a pump ID") + keyboardType = .numberPad + value = pumpID + contextHelp = NSLocalizedString("The pump ID can be found printed on the back, or near the bottom of the STATUS/Esc screen. It is the strictly numerical portion of the serial number (shown as SN or S/N).", comment: "Instructions on where to find the pump ID on a Minimed pump") + } + + override func viewDidLoad() { + super.viewDidLoad() + } + + // MARK: - Table view data source + + private enum Section: Int { + case id + case region + + static let count = 2 + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return Section.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch Section(rawValue: section)! { + case .id: + return super.tableView(tableView, numberOfRowsInSection: section) + case .region: + return PumpRegion.count + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + switch Section(rawValue: indexPath.section)! { + case .id: + return super.tableView(tableView, cellForRowAt: indexPath) + case .region: + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell") + + let region = PumpRegion(rawValue: indexPath.row)! + + cell.textLabel?.text = String(describing: region) + cell.accessoryType = self.region == region ? .checkmark : .none + + return cell + } + } + + override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { + switch Section(rawValue: section)! { + case .id: + return super.tableView(tableView, titleForFooterInSection: section) + case .region: + return NSLocalizedString("The pump regioncan be found printed on the back as part of the model number (REF), for example: MMT-551NAB, or MMT-515LWWS. If the model number contains \"NA\" or \"CA\", then the region is North America. If if contains \"WW\", then the region is World-Wide.", comment: "Instructions on selecting the pump region") + } + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + switch Section(rawValue: indexPath.section)! { + case .id: + break + case .region: + region = PumpRegion(rawValue: indexPath.row) + tableView.deselectRow(at: indexPath, animated: true) + } + } +} diff --git a/Loop/View Controllers/SettingsTableViewController.swift b/Loop/View Controllers/SettingsTableViewController.swift index b95f220dc0..db90f39cc2 100644 --- a/Loop/View Controllers/SettingsTableViewController.swift +++ b/Loop/View Controllers/SettingsTableViewController.swift @@ -16,7 +16,7 @@ private let ConfigCellIdentifier = "ConfigTableViewCell" private let TapToSetString = NSLocalizedString("Tap to set", comment: "The empty-state text for a configuration value") -final class SettingsTableViewController: UITableViewController, DailyValueScheduleTableViewControllerDelegate, TextFieldTableViewControllerDelegate { +final class SettingsTableViewController: UITableViewController, DailyValueScheduleTableViewControllerDelegate { @IBOutlet var devicesSectionTitleView: UIView! @@ -87,8 +87,9 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu fileprivate enum LoopRow: Int { case dosing = 0 case preferredInsulinDataSource + case diagnostic - static let count = 2 + static let count = 3 } fileprivate enum ConfigurationRow: Int { @@ -115,7 +116,7 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu static let count = 4 } - private lazy var valueNumberFormatter: NumberFormatter = { + fileprivate lazy var valueNumberFormatter: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .decimal @@ -165,6 +166,13 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu cell.textLabel?.text = NSLocalizedString("Preferred Data Source", comment: "The title text for the preferred insulin data source config") cell.detailTextLabel?.text = String(describing: dataManager.preferredInsulinDataSource) + return cell + case .diagnostic: + let cell = tableView.dequeueReusableCell(withIdentifier: ConfigCellIdentifier, for: indexPath) + + cell.textLabel?.text = NSLocalizedString("Issue Report", comment: "The title text for the issue report cell") + cell.detailTextLabel?.text = nil + return cell } case .configuration: @@ -203,9 +211,9 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu if let carbRatioSchedule = dataManager.carbRatioSchedule { let unit = carbRatioSchedule.unit - let value = carbRatioSchedule.averageQuantity().doubleValue(for: unit) + let value = valueNumberFormatter.string(from: NSNumber(value: carbRatioSchedule.averageQuantity().doubleValue(for: unit))) ?? "—" - configCell.detailTextLabel?.text = "\(valueNumberFormatter.string(from: NSNumber(value: value))!) \(unit)/U" + configCell.detailTextLabel?.text = String(format: NSLocalizedString("%1$@ %2$@/U", comment: "Format string for carb ratio average. (1: value)(2: carb unit)"), value, unit) } else { configCell.detailTextLabel?.text = TapToSetString } @@ -214,9 +222,9 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu if let insulinSensitivitySchedule = dataManager.insulinSensitivitySchedule { let unit = insulinSensitivitySchedule.unit - let value = insulinSensitivitySchedule.averageQuantity().doubleValue(for: unit) + let value = valueNumberFormatter.string(from: NSNumber(value: insulinSensitivitySchedule.averageQuantity().doubleValue(for: unit))) ?? "—" - configCell.detailTextLabel?.text = "\(valueNumberFormatter.string(from: NSNumber(value: value))!) \(unit)/U" + configCell.detailTextLabel?.text = String(format: NSLocalizedString("%1$@ %2$@/U", comment: "Format string for insulin sensitivity average (1: value)(2: glucose unit)"), value, unit.glucoseUnitDisplayString) } else { configCell.detailTextLabel?.text = TapToSetString } @@ -226,8 +234,10 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu if let glucoseTargetRangeSchedule = dataManager.glucoseTargetRangeSchedule { let unit = glucoseTargetRangeSchedule.unit let value = glucoseTargetRangeSchedule.value(at: Date()) + let minTarget = valueNumberFormatter.string(from: NSNumber(value: value.minValue)) ?? "—" + let maxTarget = valueNumberFormatter.string(from: NSNumber(value: value.maxValue)) ?? "—" - configCell.detailTextLabel?.text = "\(valueNumberFormatter.string(from: NSNumber(value: value.minValue))!) – \(valueNumberFormatter.string(from: NSNumber(value: value.maxValue))!) \(unit)" + configCell.detailTextLabel?.text = String(format: NSLocalizedString("%1$@ – %2$@ %3$@", comment: "Format string for glucose target range. (1: Min target)(2: Max target)(3: glucose unit)"), minTarget, maxTarget, unit.glucoseUnitDisplayString) } else { configCell.detailTextLabel?.text = TapToSetString } @@ -235,8 +245,11 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu configCell.textLabel?.text = NSLocalizedString("Insulin Action Duration", comment: "The title text for the insulin action duration value") if let insulinActionDuration = dataManager.insulinActionDuration { + let formatter = DateComponentsFormatter() + formatter.allowsFractionalUnits = true + formatter.allowedUnits = [.hour] - configCell.detailTextLabel?.text = "\(insulinActionDuration.hours) hours" + configCell.detailTextLabel?.text = formatter.string(from: insulinActionDuration) } else { configCell.detailTextLabel?.text = TapToSetString } @@ -330,7 +343,7 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu switch row { case .pumpID: - vc = .pumpID(dataManager.pumpID) + vc = PumpIDTableViewController(pumpID: dataManager.pumpID, region: dataManager.pumpState?.pumpRegion) case .transmitterID: vc = .transmitterID(dataManager.transmitterID) case .insulinActionDuration: @@ -454,7 +467,12 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu vc.delegate = self show(vc, sender: sender) - default: + case .diagnostic: + let vc = CommandResponseViewController.generateDiagnosticReport(dataManager: dataManager) + vc.title = sender?.textLabel?.text + + show(vc, sender: sender) + case .dosing: break } case .services: @@ -537,45 +555,6 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu dataManager.receiverEnabled = sender.isOn } - // MARK: - TextFieldTableViewControllerDelegate - - func textFieldTableViewControllerDidEndEditing(_ controller: TextFieldTableViewController) { - if let indexPath = controller.indexPath { - switch ConfigurationRow(rawValue: indexPath.row)! { - case .pumpID: - dataManager.pumpID = controller.value - case .transmitterID: - dataManager.transmitterID = controller.value - case .insulinActionDuration: - if let value = controller.value, let duration = valueNumberFormatter.number(from: value)?.doubleValue { - dataManager.insulinActionDuration = TimeInterval(hours: duration) - } else { - dataManager.insulinActionDuration = nil - } - case .maxBasal: - if let value = controller.value, let rate = valueNumberFormatter.number(from: value)?.doubleValue { - dataManager.maximumBasalRatePerHour = rate - } else { - dataManager.maximumBasalRatePerHour = nil - } - case .maxBolus: - if let value = controller.value, let units = valueNumberFormatter.number(from: value)?.doubleValue { - dataManager.maximumBolus = units - } else { - dataManager.maximumBolus = nil - } - default: - assertionFailure() - } - } - - tableView.reloadData() - } - - func textFieldTableViewControllerDidReturn(_ controller: TextFieldTableViewController) { - _ = navigationController?.popViewController(animated: true) - } - // MARK: - DailyValueScheduleTableViewControllerDelegate func dailyValueScheduleTableViewControllerWillFinishUpdating(_ controller: DailyValueScheduleTableViewController) { @@ -622,3 +601,58 @@ extension SettingsTableViewController: RadioSelectionTableViewControllerDelegate } } } + + +extension SettingsTableViewController: TextFieldTableViewControllerDelegate { + func textFieldTableViewControllerDidEndEditing(_ controller: TextFieldTableViewController) { + if let indexPath = controller.indexPath { + switch ConfigurationRow(rawValue: indexPath.row)! { + case .pumpID: + dataManager.pumpID = controller.value + + if let controller = controller as? PumpIDTableViewController, + let region = controller.region + { + dataManager.pumpState?.pumpRegion = region + } + case .transmitterID: + dataManager.transmitterID = controller.value + case .insulinActionDuration: + if let value = controller.value, let duration = valueNumberFormatter.number(from: value)?.doubleValue { + dataManager.insulinActionDuration = TimeInterval(hours: duration) + } else { + dataManager.insulinActionDuration = nil + } + case .maxBasal: + if let value = controller.value, let rate = valueNumberFormatter.number(from: value)?.doubleValue { + dataManager.maximumBasalRatePerHour = rate + } else { + dataManager.maximumBasalRatePerHour = nil + } + case .maxBolus: + if let value = controller.value, let units = valueNumberFormatter.number(from: value)?.doubleValue { + dataManager.maximumBolus = units + } else { + dataManager.maximumBolus = nil + } + default: + assertionFailure() + } + } + + tableView.reloadData() + } + + func textFieldTableViewControllerDidReturn(_ controller: TextFieldTableViewController) { + _ = navigationController?.popViewController(animated: true) + } +} + + +extension SettingsTableViewController: PumpIDTableViewControllerDelegate { + func pumpIDTableViewControllerDidChangePumpRegion(_ controller: PumpIDTableViewController) { + if let region = controller.region { + dataManager.pumpState?.pumpRegion = region + } + } +} diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index b340c4e89a..786718da69 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -145,10 +145,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize components.minute = 0 let date = Date(timeIntervalSinceNow: -TimeInterval(hours: max(1, historyHours))) charts.startDate = Calendar.current.nextDate(after: date, matching: components, matchingPolicy: .strict, direction: .backward) ?? date - charts.glucoseDisplayRange = ( - min: HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: 100), - max: HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: 175) - ) let reloadGroup = DispatchGroup() let oldRecommendedTempBasal = self.recommendedTempBasal @@ -321,7 +317,16 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize static let count = 4 } - private let charts = StatusChartsManager() + private lazy var charts: StatusChartsManager = { + let charts = StatusChartsManager() + + charts.glucoseDisplayRange = ( + min: HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: 100), + max: HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: 175) + ) + + return charts + }() // MARK: Glucose @@ -558,11 +563,15 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { switch Section(rawValue: indexPath.section)! { case .charts: + // 20: Status bar + // 44: Toolbar + let availableSize = max(tableView.bounds.width, tableView.bounds.height) - 20 - (tableView.tableHeaderView?.frame.height ?? 0) - 44 + switch ChartRow(rawValue: indexPath.row)! { case .glucose: - return 180 + return max(100, 0.37 * availableSize) case .iob, .dose, .cob: - return 110 + return max(100, 0.21 * availableSize) } case .status: return UITableViewAutomaticDimension @@ -731,7 +740,7 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize if let bolusViewController = segue.source as? BolusViewController { if let bolus = bolusViewController.bolus, bolus > 0 { let startDate = Date() - dataManager.enactBolus(bolus) { (error) in + dataManager.enactBolus(units: bolus) { (error) in if error != nil { NotificationManager.sendBolusFailureNotificationForAmount(bolus, atStartDate: startDate) } @@ -798,7 +807,7 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize @objc private func openCGMApp(_: Any) { if let url = cgmAppURL { - UIApplication.shared.openURL(url) + UIApplication.shared.open(url) } } diff --git a/Loop/Views/ChartContentView.swift b/Loop/Views/ChartContentView.swift index 9147d7e49e..9d2cbfe3b0 100644 --- a/Loop/Views/ChartContentView.swift +++ b/Loop/Views/ChartContentView.swift @@ -15,6 +15,8 @@ class ChartContentView: UIView { if chartView == nil || chartView!.frame != bounds { chartView = chartGenerator?(bounds) + } else if chartView!.superview == nil { + addSubview(chartView!) } } diff --git a/Loop/Views/GlucoseHUDView.swift b/Loop/Views/GlucoseHUDView.swift index 3fecce9c80..ce3c8e539f 100644 --- a/Loop/Views/GlucoseHUDView.swift +++ b/Loop/Views/GlucoseHUDView.swift @@ -37,6 +37,34 @@ final class GlucoseHUDView: HUDView { } } + private enum SensorAlertState { + case ok + case missing + case invalid + case remote + } + + private var sensorAlertState = SensorAlertState.ok { + didSet { + var alertLabelAlpha: CGFloat = 1 + + switch sensorAlertState { + case .ok: + alertLabelAlpha = 0 + case .missing, .invalid: + alertLabel.backgroundColor = UIColor.agingColor + alertLabel.text = "!" + case .remote: + alertLabel.backgroundColor = UIColor.unknownColor + alertLabel.text = "☁︎" + } + + UIView.animate(withDuration: 0.25, animations: { + self.alertLabel.alpha = alertLabelAlpha + }) + } + } + func set(_ glucoseValue: GlucoseValue, for unit: HKUnit, from sensor: SensorDisplayable?) { var accessibilityStrings = [String]() @@ -56,16 +84,19 @@ final class GlucoseHUDView: HUDView { accessibilityStrings.append(trend.localizedDescription) } - if sensor?.isStateValid == false { + if sensor == nil { + sensorAlertState = .missing + } else if sensor!.isStateValid == false { + sensorAlertState = .invalid accessibilityStrings.append(NSLocalizedString("Needs attention", comment: "Accessibility label component for glucose HUD describing an invalid state")) + } else if sensor!.isLocal == false { + sensorAlertState = .remote + } else { + sensorAlertState = .ok } unitLabel.text = unitStrings.joined(separator: " ") accessibilityValue = accessibilityStrings.joined(separator: ", ") - - UIView.animate(withDuration: 0.25, animations: { - self.alertLabel.alpha = sensor?.isStateValid == true ? 0 : 1 - }) } private lazy var timeFormatter: DateFormatter = { diff --git a/Loop/Views/SwitchTableViewCell.swift b/Loop/Views/SwitchTableViewCell.swift index 2937c3b092..9d45a292bb 100644 --- a/Loop/Views/SwitchTableViewCell.swift +++ b/Loop/Views/SwitchTableViewCell.swift @@ -17,6 +17,13 @@ final class SwitchTableViewCell: UITableViewCell { @IBOutlet var `switch`: UISwitch? + override func layoutSubviews() { + super.layoutSubviews() + + contentView.layoutMargins.left = separatorInset.left + contentView.layoutMargins.right = separatorInset.left + } + override func prepareForReuse() { super.prepareForReuse() diff --git a/Loop/gallery.ckcomplication/A307227B-6EFF-4242-A538-2C9AC617E041.json b/Loop/gallery.ckcomplication/A307227B-6EFF-4242-A538-2C9AC617E041.json new file mode 100644 index 0000000000..58580ff1b3 --- /dev/null +++ b/Loop/gallery.ckcomplication/A307227B-6EFF-4242-A538-2C9AC617E041.json @@ -0,0 +1,15 @@ +{ + "class" : "CLKComplicationTemplateModularSmallStackText", + "highlightLine2" : false, + "line2TextProvider" : { + "class" : "CLKLocalizableSimpleTextProvider", + "text" : "mg\/dL" + }, + "line1TextProvider" : { + "shortText" : "--", + "class" : "CLKSimpleTextProvider", + "text" : "--", + "accessibilityLabel" : "No glucose value available" + }, + "version" : 30000 +} \ No newline at end of file diff --git a/Loop/gallery.ckcomplication/Base.lproj/ckcomplication.strings b/Loop/gallery.ckcomplication/Base.lproj/ckcomplication.strings new file mode 100644 index 0000000000..63987e6900 --- /dev/null +++ b/Loop/gallery.ckcomplication/Base.lproj/ckcomplication.strings @@ -0,0 +1,10 @@ +/* + ckcomplication.strings + Loop + + Created by Nate Racklyeft on 9/18/16. + Copyright © 2016 Nathan Racklyeft. All rights reserved. +*/ + +/* The complication template example unit string */ +"mg/dL" = "mg/dL" diff --git a/Loop/gallery.ckcomplication/complicationManifest.json b/Loop/gallery.ckcomplication/complicationManifest.json new file mode 100644 index 0000000000..f7de58b5c8 --- /dev/null +++ b/Loop/gallery.ckcomplication/complicationManifest.json @@ -0,0 +1,6 @@ +{ + "supported complication families" : { + "0" : "A307227B-6EFF-4242-A538-2C9AC617E041.json" + }, + "client ID" : "com.loudnate.Loop.watchkitapp.watchkitextension" +} \ No newline at end of file diff --git a/LoopTests/Info.plist b/LoopTests/Info.plist index 2556bb31c6..1b43d10edc 100644 --- a/LoopTests/Info.plist +++ b/LoopTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.13.0 + 1.0rc1 CFBundleSignature ???? CFBundleVersion diff --git a/README.md b/README.md index bea9f7f9ac..12756e1354 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ![App Icon](/Loop/Assets.xcassets/AppIcon.appiconset/Icon-40%402x.png?raw=true) ![WatchApp Icon](/WatchApp/Assets.xcassets/AppIcon.appiconset/watch-40%402x.png?raw=true) -[![Build Status](https://travis-ci.org/loudnate/Loop.svg?branch=master)](https://travis-ci.org/loudnate/Loop) -[![Join the chat at https://gitter.im/loudnate/LoopKit](https://badges.gitter.im/loudnate/LoopKit.svg)](https://gitter.im/loudnate/LoopKit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/LoopKit/Loop.svg?branch=master)](https://travis-ci.org/LoopKit/Loop) +[![Join the chat at https://gitter.im/LoopKit/LoopKit](https://badges.gitter.im/LoopKit/LoopKit.svg)](https://gitter.im/LoopKit/LoopKit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Loop is an app template for building an artificial pancreas. It is a stone resting on the boulders of work done by [@bewest](https://github.com/bewest/decoding-carelink), [@ps2](https://github.com/ps2/rileylink) and many others. @@ -72,7 +72,7 @@ Bluetooth LE communication with Minimed pumps is enabled by the [RileyLink](http ### LoopKit -Loop is built on top of [LoopKit](https://github.com/loudnate/LoopKit). LoopKit is a set of frameworks that provide data storage, retrieval, and calcluation, as well as boilerplate view controllers used in Loop. +Loop is built on top of [LoopKit](https://github.com/LoopKit/LoopKit). LoopKit is a set of frameworks that provide data storage, retrieval, and calcluation, as well as boilerplate view controllers used in Loop. # Getting Started @@ -103,7 +103,7 @@ Loop optionally supports select third-party remote services, which are configure # Making it Your Own -[Please visit the Wiki for more info on customizing the app](https://github.com/loudnate/Loop/wiki/Personalizing-Your-App-Name-&-Icon) +[Please visit the Wiki for more info on customizing the app](https://github.com/LoopKit/Loop/wiki/Personalizing-Your-App-Name-&-Icon) # License and Code of Conduct diff --git a/WatchApp Extension/Base.lproj/ckcomplication.strings b/WatchApp Extension/Base.lproj/ckcomplication.strings new file mode 100644 index 0000000000..63987e6900 --- /dev/null +++ b/WatchApp Extension/Base.lproj/ckcomplication.strings @@ -0,0 +1,10 @@ +/* + ckcomplication.strings + Loop + + Created by Nate Racklyeft on 9/18/16. + Copyright © 2016 Nathan Racklyeft. All rights reserved. +*/ + +/* The complication template example unit string */ +"mg/dL" = "mg/dL" diff --git a/WatchApp Extension/ComplicationController.swift b/WatchApp Extension/ComplicationController.swift index 45192084b4..63884fbbf0 100644 --- a/WatchApp Extension/ComplicationController.swift +++ b/WatchApp Extension/ComplicationController.swift @@ -7,6 +7,7 @@ // import ClockKit +import WatchKit final class ComplicationController: NSObject, CLKComplicationDataSource { @@ -18,7 +19,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { } func getTimelineStartDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) { - if let date = DeviceDataManager.sharedManager.lastContextData?.glucoseDate { + if let date = ExtensionDelegate.shared().lastContext?.glucoseDate { handler(date as Date) } else { handler(nil) @@ -26,7 +27,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { } func getTimelineEndDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) { - if let date = DeviceDataManager.sharedManager.lastContextData?.glucoseDate { + if let date = ExtensionDelegate.shared().lastContext?.glucoseDate { handler(date as Date) } else { handler(nil) @@ -45,7 +46,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { switch complication.family { case .modularSmall: - if let context = DeviceDataManager.sharedManager.lastContextData, + if let context = ExtensionDelegate.shared().lastContext, let glucose = context.glucose, let unit = context.preferredGlucoseUnit, let glucoseString = formatter.string(from: NSNumber(value: glucose.doubleValue(for: unit))), @@ -68,7 +69,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: (@escaping ([CLKComplicationTimelineEntry]?) -> Void)) { // Call the handler with the timeline entries after to the given date - if let context = DeviceDataManager.sharedManager.lastContextData, + if let context = ExtensionDelegate.shared().lastContext, let glucose = context.glucose, let unit = context.preferredGlucoseUnit, let glucoseString = formatter.string(from: NSNumber(value: glucose.doubleValue(for: unit))), @@ -81,35 +82,19 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { } } - func requestedUpdateDidBegin() { - DeviceDataManager.sharedManager.updateComplicationDataIfNeeded() - } - - func requestedUpdateBudgetExhausted() { - // TODO: os_log_info in iOS 10 - } - - // MARK: - Update Scheduling - - func getNextRequestedUpdateDate(handler: @escaping (Date?) -> Void) { - // Call the handler with the date when you would next like to be given the opportunity to update your complication content - handler(Date(timeIntervalSinceNow: TimeInterval(2 * 60 * 60))) - } - // MARK: - Placeholder Templates - - func getPlaceholderTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { + + func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { switch complication.family { case .modularSmall: let template = CLKComplicationTemplateModularSmallStackText() template.line1TextProvider = CLKSimpleTextProvider(text: "--", shortText: "--", accessibilityLabel: "No glucose value available") - template.line2TextProvider = CLKSimpleTextProvider(text: "mg/dL") + template.line2TextProvider = CLKSimpleTextProvider.localizableTextProvider(withStringsFileTextKey: "mg/dL") handler(template) default: handler(nil) } } - } diff --git a/WatchApp Extension/Controllers/AddCarbsInterfaceController.swift b/WatchApp Extension/Controllers/AddCarbsInterfaceController.swift index 03cf417831..f4c84a9157 100644 --- a/WatchApp Extension/Controllers/AddCarbsInterfaceController.swift +++ b/WatchApp Extension/Controllers/AddCarbsInterfaceController.swift @@ -7,12 +7,26 @@ // import WatchKit -import Foundation +import WatchConnectivity final class AddCarbsInterfaceController: WKInterfaceController, IdentifiableClass { - private var carbValue: Int = 15 + fileprivate var carbValue: Int = 15 { + didSet { + guard carbValue >= 0 else { + carbValue = 0 + return + } + + guard carbValue <= 100 else { + carbValue = 100 + return + } + + valueLabel.setText(String(carbValue)) + } + } private var absorptionTime = AbsorptionTimeType.medium { didSet { @@ -33,8 +47,6 @@ final class AddCarbsInterfaceController: WKInterfaceController, IdentifiableClas @IBOutlet var valueLabel: WKInterfaceLabel! - @IBOutlet var valuePicker: WKInterfacePicker! - @IBOutlet var absorptionButtonA: WKInterfaceButton! @IBOutlet var absorptionButtonB: WKInterfaceButton! @@ -45,12 +57,7 @@ final class AddCarbsInterfaceController: WKInterfaceController, IdentifiableClas super.awake(withContext: context) // Configure interface objects here. - - let items = (0...100).map { _ in WKPickerItem() } - - valuePicker.setItems(items) - - valuePicker.setSelectedItemIndex(carbValue) + crownSequencer.delegate = self absorptionTime = .medium } @@ -59,7 +66,7 @@ final class AddCarbsInterfaceController: WKInterfaceController, IdentifiableClas // This method is called when watch view controller is about to be visible to user super.willActivate() - valuePicker.focus() + crownSequencer.focus() } override func didDeactivate() { @@ -69,17 +76,12 @@ final class AddCarbsInterfaceController: WKInterfaceController, IdentifiableClas // MARK: - Actions - @IBAction func pickerValueUpdated(_ value: Int) { - carbValue = value - valueLabel.setText(String(value)) - } - @IBAction func decrement() { - valuePicker.setSelectedItemIndex(carbValue - 5) + carbValue -= 5 } @IBAction func increment() { - valuePicker.setSelectedItemIndex(carbValue + 5) + carbValue += 5 } @IBAction func setAbsorptionTimeFast() { @@ -98,10 +100,41 @@ final class AddCarbsInterfaceController: WKInterfaceController, IdentifiableClas if carbValue > 0 { let entry = CarbEntryUserInfo(value: Double(carbValue), absorptionTimeType: absorptionTime, startDate: Date()) - DeviceDataManager.sharedManager.sendCarbEntry(entry) + do { + try WCSession.default().sendCarbEntryMessage(entry, + replyHandler: { (suggestion) in + WKExtension.shared().rootInterfaceController?.presentController(withName: BolusInterfaceController.className, context: suggestion) + }, + errorHandler: { (error) in + ExtensionDelegate.shared().present(error) + } + ) + } catch { + presentAlert(withTitle: NSLocalizedString("Send Failed", comment: "The title of the alert controller displayed after a carb entry send attempt fails"), + message: NSLocalizedString("Make sure your iPhone is nearby and try again", comment: "The recovery message displayed after a carb entry send attempt fails"), + preferredStyle: .alert, + actions: [WKAlertAction.dismissAction()] + ) + return + } } dismiss() } + // MARK: - Crown Sequencer + + fileprivate var accumulatedRotation: Double = 0 +} + +fileprivate let rotationsPerCarb: Double = 1/24 + +extension AddCarbsInterfaceController: WKCrownDelegate { + func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) { + accumulatedRotation += rotationalDelta + + let remainder = accumulatedRotation.truncatingRemainder(dividingBy: rotationsPerCarb) + carbValue += Int((accumulatedRotation - remainder).divided(by: rotationsPerCarb)) + accumulatedRotation = remainder + } } diff --git a/WatchApp Extension/Controllers/BolusInterfaceController.swift b/WatchApp Extension/Controllers/BolusInterfaceController.swift index 4462a7fc9c..34931d463d 100644 --- a/WatchApp Extension/Controllers/BolusInterfaceController.swift +++ b/WatchApp Extension/Controllers/BolusInterfaceController.swift @@ -8,12 +8,25 @@ import WatchKit import Foundation +import WatchConnectivity final class BolusInterfaceController: WKInterfaceController, IdentifiableClass { - private var bolusValue: Double = 0 { + fileprivate var pickerValue: Int = 0 { didSet { + guard pickerValue >= 0 else { + pickerValue = 0 + return + } + + guard pickerValue <= maxPickerValue else { + pickerValue = maxPickerValue + return + } + + let bolusValue = bolusValueFromPickerValue(pickerValue) + switch bolusValue { case let x where x < 1: formatter.minimumFractionDigits = 3 @@ -57,20 +70,19 @@ final class BolusInterfaceController: WKInterfaceController, IdentifiableClass { return formatter }() + private var maxPickerValue = 0 + /// 1.25 @IBOutlet var valueLabel: WKInterfaceLabel! - @IBOutlet var valuePicker: WKInterfacePicker! - /// REC: 2.25 U @IBOutlet var recommendedValueLabel: WKInterfaceLabel! override func awake(withContext context: Any?) { super.awake(withContext: context) - let maxPickerValue: Int var maxBolusValue: Double = 15 - let pickerValue: Int + var pickerValue = 0 if let context = context as? BolusSuggestionUserInfo { let recommendedBolus = context.recommendedBolus @@ -81,31 +93,25 @@ final class BolusInterfaceController: WKInterfaceController, IdentifiableClass { maxBolusValue = recommendedBolus } - maxPickerValue = pickerValueFromBolusValue(maxBolusValue) let recommendedPickerValue = pickerValueFromBolusValue(recommendedBolus) - maxBolusValue = bolusValueFromPickerValue(maxPickerValue) pickerValue = Int(Double(recommendedPickerValue) * 0.75) - bolusValue = bolusValueFromPickerValue(pickerValue) if let valueString = formatter.string(from: NSNumber(value: recommendedBolus)) { recommendedValueLabel.setText(String(format: NSLocalizedString("Rec: %@ U", comment: "The label and value showing the recommended bolus"), valueString).localizedUppercase) } - } else { - maxPickerValue = pickerValueFromBolusValue(maxBolusValue) - pickerValue = pickerValueFromBolusValue(bolusValue) - bolusValue = 0 } - let items = (0...maxPickerValue).map { _ in WKPickerItem() } - valuePicker.setItems(items) - valuePicker.setSelectedItemIndex(pickerValue) + self.maxPickerValue = pickerValueFromBolusValue(maxBolusValue) + self.pickerValue = pickerValue + + crownSequencer.delegate = self } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() - valuePicker.focus() + crownSequencer.focus() } override func didDeactivate() { @@ -115,35 +121,51 @@ final class BolusInterfaceController: WKInterfaceController, IdentifiableClass { // MARK: - Actions - @IBAction func pickerValueUpdated(_ value: Int) { - bolusValue = bolusValueFromPickerValue(value) - } - @IBAction func decrement() { - valuePicker.setSelectedItemIndex(pickerValueFromBolusValue(bolusValue) - 10) + pickerValue -= 10 } @IBAction func increment() { - valuePicker.setSelectedItemIndex(pickerValueFromBolusValue(bolusValue) + 10) + pickerValue += 10 } @IBAction func deliver() { + let bolusValue = bolusValueFromPickerValue(pickerValue) + if bolusValue > 0 { let bolus = SetBolusUserInfo(value: bolusValue, startDate: Date()) + do { - try DeviceDataManager.sharedManager.sendSetBolus(bolus) - } catch DeviceDataManagerError.reachabilityError { - presentAlert(withTitle: NSLocalizedString("Bolus Failed", comment: "The title of the alert controller displayed after a bolus attempt fails"), + try WCSession.default().sendBolusMessage(bolus) { (error) in + ExtensionDelegate.shared().present(error) + } + } catch { + presentAlert( + withTitle: NSLocalizedString("Bolus Failed", comment: "The title of the alert controller displayed after a bolus attempt fails"), message: NSLocalizedString("Make sure your iPhone is nearby and try again", comment: "The recovery message displayed after a bolus attempt fails"), preferredStyle: .alert, actions: [WKAlertAction.dismissAction()] ) return - } catch { } } dismiss() } + // MARK: - Crown Sequencer + + fileprivate var accumulatedRotation: Double = 0 +} + +fileprivate let rotationsPerValue: Double = 1/24 + +extension BolusInterfaceController: WKCrownDelegate { + func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) { + accumulatedRotation += rotationalDelta + + let remainder = accumulatedRotation.truncatingRemainder(dividingBy: rotationsPerValue) + pickerValue += Int((accumulatedRotation - remainder).divided(by: rotationsPerValue)) + accumulatedRotation = remainder + } } diff --git a/WatchApp Extension/Controllers/ContextInterfaceController.swift b/WatchApp Extension/Controllers/ContextInterfaceController.swift deleted file mode 100644 index f9cb4e684a..0000000000 --- a/WatchApp Extension/Controllers/ContextInterfaceController.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ContextInterfaceController.swift -// Loop -// -// Created by Nathan Racklyeft on 5/29/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import WatchKit -import Foundation - - -class ContextInterfaceController: WKInterfaceController { - - let dataManager = DeviceDataManager.sharedManager - - private var lastContextDataObserverContext = 0 - - override func awake(withContext context: Any?) { - super.awake(withContext: context) - - // Configure interface objects here. - } - - override func willActivate() { - super.willActivate() - - dataManager.addObserver(self, forKeyPath: "lastContextData", options: [], context: &lastContextDataObserverContext) - - updateFromContext(dataManager.lastContextData) - } - - override func didDeactivate() { - super.didDeactivate() - - dataManager.removeObserver(self, forKeyPath: "lastContextData", context: &lastContextDataObserverContext) - - } - - func updateFromContext(_ context: WatchContext?) { - DeviceDataManager.sharedManager.updateComplicationDataIfNeeded() - } - - // MARK: - KVO - - override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - if context == &lastContextDataObserverContext { - if let context = dataManager.lastContextData { - updateFromContext(context) - } - } else { - super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) - } - } - -} diff --git a/WatchApp Extension/Controllers/ContextUpdatable.swift b/WatchApp Extension/Controllers/ContextUpdatable.swift new file mode 100644 index 0000000000..00cc2100d7 --- /dev/null +++ b/WatchApp Extension/Controllers/ContextUpdatable.swift @@ -0,0 +1,14 @@ +// +// ContextUpdatable.swift +// Loop +// +// Created by Nate Racklyeft on 9/19/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import Foundation + + +protocol ContextUpdatable { + func update(with context: WatchContext?) +} diff --git a/WatchApp Extension/Controllers/NotificationController.swift b/WatchApp Extension/Controllers/NotificationController.swift index 60226b4aef..0cc9b0011e 100644 --- a/WatchApp Extension/Controllers/NotificationController.swift +++ b/WatchApp Extension/Controllers/NotificationController.swift @@ -30,10 +30,8 @@ final class NotificationController: WKUserNotificationInterfaceController { super.didDeactivate() } - /* override func didReceive(_ notification: UNNotification, withCompletion completionHandler: @escaping (WKUserNotificationInterfaceType) -> Void) { completionHandler(.default) } - */ } diff --git a/WatchApp Extension/Controllers/StatusInterfaceController.swift b/WatchApp Extension/Controllers/StatusInterfaceController.swift index e2fc48d7fa..a5771c4bc6 100644 --- a/WatchApp Extension/Controllers/StatusInterfaceController.swift +++ b/WatchApp Extension/Controllers/StatusInterfaceController.swift @@ -10,7 +10,7 @@ import WatchKit import Foundation -final class StatusInterfaceController: ContextInterfaceController { +final class StatusInterfaceController: WKInterfaceController, ContextUpdatable { @IBOutlet var graphImage: WKInterfaceImage! @IBOutlet var loopHUDImage: WKInterfaceImage! @@ -19,59 +19,57 @@ final class StatusInterfaceController: ContextInterfaceController { @IBOutlet var eventualGlucoseLabel: WKInterfaceLabel! @IBOutlet var statusLabel: WKInterfaceLabel! - override func updateFromContext(_ context: WatchContext?) { - super.updateFromContext(context) + private var lastContext: WatchContext? - resetInterface() + func update(with context: WatchContext?) { + lastContext = context - DispatchQueue.main.async { - if let date = context?.loopLastRunDate { - self.loopTimer.setDate(date as Date) - self.loopTimer.setHidden(false) - self.loopTimer.start() + if let date = context?.loopLastRunDate { + self.loopTimer.setDate(date as Date) + self.loopTimer.setHidden(false) + self.loopTimer.start() - let loopImage: LoopImage + let loopImage: LoopImage - switch date.timeIntervalSinceNow { - case let t where t.minutes <= 5: - loopImage = .Fresh - case let t where t.minutes <= 15: - loopImage = .Aging - default: - loopImage = .Stale - } - - self.loopHUDImage.setLoopImage(loopImage) + switch date.timeIntervalSinceNow { + case let t where t.minutes <= 5: + loopImage = .Fresh + case let t where t.minutes <= 15: + loopImage = .Aging + default: + loopImage = .Stale } + + self.loopHUDImage.setLoopImage(loopImage) + } else { + loopTimer.setHidden(true) + loopHUDImage.setLoopImage(.Unknown) } let numberFormatter = NumberFormatter() - DispatchQueue.main.async { - if let glucose = context?.glucose, let unit = context?.preferredGlucoseUnit { - let glucoseValue = glucose.doubleValue(for: unit) - let trend = context?.glucoseTrend?.symbol ?? "" + if let glucose = context?.glucose, let unit = context?.preferredGlucoseUnit { + let glucoseValue = glucose.doubleValue(for: unit) + let trend = context?.glucoseTrend?.symbol ?? "" - self.glucoseLabel.setText((numberFormatter.string(from: NSNumber(value: glucoseValue)) ?? "") + trend) - self.glucoseLabel.setHidden(false) - } + self.glucoseLabel.setText((numberFormatter.string(from: NSNumber(value: glucoseValue)) ?? "") + trend) + self.glucoseLabel.setHidden(false) + } else { + glucoseLabel.setHidden(true) + } - if let eventualGlucose = context?.eventualGlucose, let unit = context?.preferredGlucoseUnit { - let glucoseValue = eventualGlucose.doubleValue(for: unit) + if let eventualGlucose = context?.eventualGlucose, let unit = context?.preferredGlucoseUnit { + let glucoseValue = eventualGlucose.doubleValue(for: unit) - self.eventualGlucoseLabel.setText(numberFormatter.string(from: NSNumber(value: glucoseValue))) - self.eventualGlucoseLabel.setHidden(false) - } + self.eventualGlucoseLabel.setText(numberFormatter.string(from: NSNumber(value: glucoseValue))) + self.eventualGlucoseLabel.setHidden(false) + } else { + eventualGlucoseLabel.setHidden(true) } - } - private func resetInterface() { - loopTimer.setHidden(true) + // TODO: Other elements statusLabel.setHidden(true) graphImage.setHidden(true) - glucoseLabel.setHidden(true) - eventualGlucoseLabel.setHidden(true) - loopHUDImage.setLoopImage(.Unknown) } // MARK: - Menu Items @@ -81,7 +79,7 @@ final class StatusInterfaceController: ContextInterfaceController { } @IBAction func setBolus() { - presentController(withName: BolusInterfaceController.className, context: dataManager.lastContextData?.bolusSuggestion) + presentController(withName: BolusInterfaceController.className, context: lastContext?.bolusSuggestion) } } diff --git a/WatchApp Extension/DeviceDataManager.swift b/WatchApp Extension/DeviceDataManager.swift deleted file mode 100644 index bc9d0fc0c3..0000000000 --- a/WatchApp Extension/DeviceDataManager.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// DeviceDataManager.swift -// Naterade -// -// Created by Nathan Racklyeft on 9/24/15. -// Copyright © 2015 Nathan Racklyeft. All rights reserved. -// - -import ClockKit -import Foundation -import WatchConnectivity -import WatchKit - - -enum DeviceDataManagerError: Error { - case reachabilityError -} - - -final class DeviceDataManager: NSObject, WCSessionDelegate { - - private var connectSession: WCSession? - - private func readContext() -> WatchContext? { - return UserDefaults.standard.watchContext - } - - private func saveContext(_ context: WatchContext) { - UserDefaults.standard.watchContext = context - } - - private var complicationDataLastRefreshed: Date { - get { - return UserDefaults.standard.complicationDataLastRefreshed - } - set { - UserDefaults.standard.complicationDataLastRefreshed = newValue - } - } - - private var hasNewComplicationData: Bool { - get { - return UserDefaults.standard.watchContextReadyForComplication - } - set { - UserDefaults.standard.watchContextReadyForComplication = newValue - } - } - - dynamic var lastContextData: WatchContext? { - didSet { - if let data = lastContextData { - saveContext(data) - } - } - } - - func updateComplicationDataIfNeeded() { - if DeviceDataManager.sharedManager.hasNewComplicationData { - DeviceDataManager.sharedManager.hasNewComplicationData = false - let server = CLKComplicationServer.sharedInstance() - for complication in server.activeComplications ?? [] { - if complicationDataLastRefreshed.timeIntervalSinceNow < TimeInterval(-8 * 60 * 60) { - complicationDataLastRefreshed = Date() - server.reloadTimeline(for: complication) - } else { - server.extendTimeline(for: complication) - } - } - } - } - - func sendCarbEntry(_ carbEntry: CarbEntryUserInfo) { - guard let session = connectSession else { return } - - if session.isReachable { - var replied = false - - session.sendMessage(carbEntry.rawValue, - replyHandler: { (reply) -> Void in - replied = true - - if let suggestion = BolusSuggestionUserInfo(rawValue: reply as BolusSuggestionUserInfo.RawValue), suggestion.recommendedBolus > 0 { - WKExtension.shared().rootInterfaceController?.presentController(withName: BolusInterfaceController.className, context: suggestion) - } - }, - errorHandler: { (error) -> Void in - if !replied { - WKExtension.shared().rootInterfaceController?.presentAlert(withTitle: #function, message: (error as NSError).localizedRecoverySuggestion, preferredStyle: .alert, actions: [WKAlertAction.dismissAction()]) - } - } - ) - } else { - session.transferUserInfo(carbEntry.rawValue) - } - } - - func sendSetBolus(_ userInfo: SetBolusUserInfo) throws { - guard let session = connectSession, session.isReachable else { - throw DeviceDataManagerError.reachabilityError - } - - var replied = false - - session.sendMessage(userInfo.rawValue, replyHandler: { (reply) -> Void in - replied = true - }, errorHandler: { (error) -> Void in - if !replied { - WKExtension.shared().rootInterfaceController?.presentAlert(withTitle: error.localizedDescription, message: (error as NSError).localizedRecoverySuggestion ?? (error as NSError).localizedFailureReason, preferredStyle: .alert, actions: [WKAlertAction.dismissAction()]) - } - }) - } - - // MARK: - WCSessionDelegate - - func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { -// if let error = error { - // TODO: os_log_info in iOS 10 -// } - } - - func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { - if let context = WatchContext(rawValue: applicationContext as WatchContext.RawValue) { - lastContextData = context - } - } - - func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { - switch userInfo["name"] as? String { - case .some: - break - default: - if let context = WatchContext(rawValue: userInfo as WatchContext.RawValue) { - lastContextData = context - updateComplicationDataIfNeeded() - } - } - } - - // MARK: - Initialization - - static let sharedManager = DeviceDataManager() - - override init() { - super.init() - - connectSession = WCSession.default() - connectSession?.delegate = self - connectSession?.activate() - - if let context = readContext() { - self.lastContextData = context - } - } -} diff --git a/WatchApp Extension/ExtensionDelegate.swift b/WatchApp Extension/ExtensionDelegate.swift index 90266a3152..0e00462b73 100644 --- a/WatchApp Extension/ExtensionDelegate.swift +++ b/WatchApp Extension/ExtensionDelegate.swift @@ -6,17 +6,48 @@ // Copyright © 2015 Nathan Racklyeft. All rights reserved. // +import WatchConnectivity import WatchKit +import os final class ExtensionDelegate: NSObject, WKExtensionDelegate { + static func shared() -> ExtensionDelegate { + return WKExtension.shared().extensionDelegate + } + + override init() { + super.init() + + let session = WCSession.default() + session.delegate = self + + // It seems, according to [this sample code](https://developer.apple.com/library/prerelease/content/samplecode/QuickSwitch/Listings/QuickSwitch_WatchKit_Extension_ExtensionDelegate_swift.html#//apple_ref/doc/uid/TP40016647-QuickSwitch_WatchKit_Extension_ExtensionDelegate_swift-DontLinkElementID_8) + // that WCSession activation and delegation and WKWatchConnectivityRefreshBackgroundTask don't have any determinism, + // and that KVO is the "recommended" way to deal with it. + session.addObserver(self, forKeyPath: "activationState", options: [], context: nil) + session.addObserver(self, forKeyPath: "hasContentPending", options: [], context: nil) + + session.activate() + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + DispatchQueue.main.async { + self.completePendingConnectivityTasksIfNeeded() + } + } + func applicationDidFinishLaunching() { // Perform any final initialization of your application. } func applicationDidBecomeActive() { // 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. + + if WCSession.default().activationState != .activated { + WCSession.default().activate() + } } func applicationWillResignActive() { @@ -24,4 +55,138 @@ final class ExtensionDelegate: NSObject, WKExtensionDelegate { // Use this method to pause ongoing tasks, disable timers, etc. } + func handleUserActivity(_ userInfo: [AnyHashable : Any]?) { + // Use it to respond to Handoff–related activity. WatchKit calls this method when your app is launched as a result of a Handoff action. Use the information in the provided userInfo dictionary to determine how you want to respond to the action. For example, you might decide to display a specific interface controller. + } + + func handle(_ backgroundTasks: Set) { + for task in backgroundTasks { + switch task { + case is WKApplicationRefreshBackgroundTask: + os_log("Processing WKApplicationRefreshBackgroundTask") + // Use the WKApplicationRefreshBackgroundTask class to update your app’s state in the background. + // You often use a background app refresh task to drive other tasks. For example, you could use a background app refresh task to start an URLSession background transfer, or to schedule a background snapshot refresh task. + // Your app must schedule background app refresh tasks by calling your extension’s scheduleBackgroundRefresh(withPreferredDate:userInfo:scheduledCompletion:) method. The system never schedules these tasks. + // WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate:userInfo: scheduledCompletion:) + // For more information, see [WKApplicationRefreshBackgroundTask] https://developer.apple.com/reference/watchkit/wkapplicationrefreshbackgroundtask + // Background app refresh tasks are budgeted. In general, the system performs approximately one task per hour for each app in the dock (including the most recently used app). This budget is shared among all apps on the dock. The system performs multiple tasks an hour for each app with a complication on the active watch face. This budget is shared among all complications on the watch face. After you exhaust the budget, the system delays your requests until more time becomes available. + break + case let task as WKSnapshotRefreshBackgroundTask: + os_log("Processing WKSnapshotRefreshBackgroundTask") + // Use the WKSnapshotRefreshBackgroundTask class to update your app’s user interface. You can push, pop, or present other interface controllers, and then update the content of the desired interface controller. The system automatically takes a snapshot of your user interface as soon as this task completes. + // Your app can invalidate its current snapshot and schedule a background snapshot refresh tasks by calling your extension’s scheduleSnapshotRefresh(withPreferredDate:userInfo:scheduledCompletion:) method. The system will also schedule background snapshot refresh tasks to periodically update your snapshot. + // For more information, see WKSnapshotRefreshBackgroundTask. + // For more information about snapshots, see Snapshots. + + task.setTaskCompleted(restoredDefaultState: false, estimatedSnapshotExpiration: Date(timeIntervalSinceNow: TimeInterval(minutes: 5)), userInfo: nil) + return // Don't call the standard setTaskCompleted handler + case is WKURLSessionRefreshBackgroundTask: + // Use the WKURLSessionRefreshBackgroundTask class to respond to URLSession background transfers. + break + case let task as WKWatchConnectivityRefreshBackgroundTask: + os_log("Processing WKWatchConnectivityRefreshBackgroundTask") + // Use the WKWatchConnectivityRefreshBackgroundTask class to receive background updates from the WatchConnectivity framework. + // For more information, see WKWatchConnectivityRefreshBackgroundTask. + + pendingConnectivityTasks.append(task) + + if WCSession.default().activationState != .activated { + WCSession.default().activate() + } + + completePendingConnectivityTasksIfNeeded() + return // Defer calls to the setTaskCompleted handler + default: + break + } + + task.setTaskCompleted() + } + } + + private var pendingConnectivityTasks: [WKWatchConnectivityRefreshBackgroundTask] = [] + + private func completePendingConnectivityTasksIfNeeded() { + if WCSession.default().activationState == .activated && !WCSession.default().hasContentPending { + pendingConnectivityTasks.forEach { $0.setTaskCompleted() } + pendingConnectivityTasks.removeAll() + } + } + + // Main queue only + private(set) var lastContext: WatchContext? { + didSet { + WKExtension.shared().rootUpdatableInterfaceController?.update(with: lastContext) + + if WKExtension.shared().applicationState != .active { + WKExtension.shared().scheduleSnapshotRefresh(withPreferredDate: Date(), userInfo: nil) { (_) in } + } + + // Update complication data if needed + let server = CLKComplicationServer.sharedInstance() + for complication in server.activeComplications ?? [] { + // In watchOS 2, we forced a timeline reload every 8 hours because attempting to extend it indefinitely seemed to lead to the complication "freezing". + if UserDefaults.standard.complicationDataLastRefreshed.timeIntervalSinceNow < TimeInterval(hours: -8) { + UserDefaults.standard.complicationDataLastRefreshed = Date() + os_log("Reloading complication timeline") + server.reloadTimeline(for: complication) + } else { + os_log("Extending complication timeline") + // TODO: Switch this back to extendTimeline if things are working correctly. + // Time Travel appears to be disabled by default in watchOS 3 anyway + server.reloadTimeline(for: complication) + } + } + } + } + + fileprivate func updateContext(_ data: [String: Any]) { + if let context = WatchContext(rawValue: data as WatchContext.RawValue) { + DispatchQueue.main.async { + self.lastContext = context + } + } + } +} + + +extension ExtensionDelegate: WCSessionDelegate { + func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { + if activationState == .activated && lastContext == nil { + updateContext(session.receivedApplicationContext) + } + } + + func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { + updateContext(applicationContext) + } + + func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { + // WatchContext is the only userInfo type without a "name" key. This isn't a great heuristic. + if !(userInfo["name"] is String) { + updateContext(userInfo) + } + } +} + + +extension ExtensionDelegate { + + /// Global shortcut to present an alert for a specific error out-of-context with a specific interface controller. + /// + /// - parameter error: The error whose contents to display + func present(_ error: Error) { + WKExtension.shared().rootInterfaceController?.presentAlert(withTitle: error.localizedDescription, message: (error as NSError).localizedRecoverySuggestion ?? (error as NSError).localizedFailureReason, preferredStyle: .alert, actions: [WKAlertAction.dismissAction()]) + } +} + + +fileprivate extension WKExtension { + var extensionDelegate: ExtensionDelegate! { + return delegate as? ExtensionDelegate + } + + var rootUpdatableInterfaceController: ContextUpdatable? { + return rootInterfaceController as? ContextUpdatable + } } diff --git a/WatchApp Extension/Extensions/NSUserDefaults.swift b/WatchApp Extension/Extensions/NSUserDefaults.swift index 99d4cb77b6..b45ba55feb 100644 --- a/WatchApp Extension/Extensions/NSUserDefaults.swift +++ b/WatchApp Extension/Extensions/NSUserDefaults.swift @@ -12,8 +12,6 @@ import Foundation extension UserDefaults { private enum Key: String { case ComplicationDataLastRefreshed = "com.loudnate.Naterade.ComplicationDataLastRefreshed" - case WatchContext = "com.loudnate.Naterade.WatchContext" - case WatchContextReadyForComplication = "com.loudnate.Naterade.WatchContextReadyForComplication" } var complicationDataLastRefreshed: Date { @@ -24,28 +22,4 @@ extension UserDefaults { set(newValue, forKey: Key.ComplicationDataLastRefreshed.rawValue) } } - - var watchContext: WatchContext? { - get { - if let rawValue = dictionary(forKey: Key.WatchContext.rawValue) { - return WatchContext(rawValue: rawValue as WatchContext.RawValue) - } else { - return nil - } - } - set { - set(newValue?.rawValue, forKey: Key.WatchContext.rawValue) - - watchContextReadyForComplication = newValue != nil - } - } - - var watchContextReadyForComplication: Bool { - get { - return bool(forKey: Key.WatchContextReadyForComplication.rawValue) - } - set { - set(newValue, forKey: Key.WatchContextReadyForComplication.rawValue) - } - } } diff --git a/WatchApp Extension/Extensions/WCSession.swift b/WatchApp Extension/Extensions/WCSession.swift new file mode 100644 index 0000000000..346b7ced61 --- /dev/null +++ b/WatchApp Extension/Extensions/WCSession.swift @@ -0,0 +1,58 @@ +// +// WCSession.swift +// Loop +// +// Created by Nate Racklyeft on 9/20/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import WatchConnectivity + + +enum MessageError: Error { + case activationError + case decodingError + case reachabilityError +} + + +extension WCSession { + func sendCarbEntryMessage(_ carbEntry: CarbEntryUserInfo, replyHandler: @escaping (BolusSuggestionUserInfo) -> Void, errorHandler: @escaping (Error) -> Void) throws { + guard activationState == .activated else { + throw MessageError.activationError + } + + guard isReachable else { + transferUserInfo(carbEntry.rawValue) + return + } + + sendMessage(carbEntry.rawValue, + replyHandler: { (reply) in + guard let suggestion = BolusSuggestionUserInfo(rawValue: reply as BolusSuggestionUserInfo.RawValue) else { + errorHandler(MessageError.decodingError) + return + } + + replyHandler(suggestion) + }, + errorHandler: errorHandler + ) + } + + func sendBolusMessage(_ userInfo: SetBolusUserInfo, errorHandler: @escaping (Error) -> Void) throws { + guard activationState == .activated else { + throw MessageError.activationError + } + + guard isReachable else { + throw MessageError.reachabilityError + } + + sendMessage(userInfo.rawValue, + replyHandler: { (reply) in + }, + errorHandler: errorHandler + ) + } +} diff --git a/WatchApp Extension/Info.plist b/WatchApp Extension/Info.plist index d5a7729405..87372e745f 100644 --- a/WatchApp Extension/Info.plist +++ b/WatchApp Extension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.13.0 + 1.0rc1 CFBundleSignature ???? CFBundleVersion diff --git a/WatchApp/Base.lproj/Interface.storyboard b/WatchApp/Base.lproj/Interface.storyboard index c9f0a77912..f58c6910b6 100644 --- a/WatchApp/Base.lproj/Interface.storyboard +++ b/WatchApp/Base.lproj/Interface.storyboard @@ -1,8 +1,8 @@ - - + + - - + + @@ -12,18 +12,13 @@ - - - - - -