Skip to content

Commit e4eeffe

Browse files
authored
Merge pull request #1534 from LoopKit/profile-expiration-alert
Port profile expiration alert to dev
2 parents 16e0647 + 4abb5ec commit e4eeffe

File tree

4 files changed

+81
-1
lines changed

4 files changed

+81
-1
lines changed

Loop.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@
508508
C1DE5D23251BFC4D00439E49 /* SimpleBolusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DE5D22251BFC4D00439E49 /* SimpleBolusView.swift */; };
509509
C1E2773E224177C000354103 /* ClockKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1E2773D224177C000354103 /* ClockKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
510510
C1E2774822433D7A00354103 /* MKRingProgressView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1E2774722433D7A00354103 /* MKRingProgressView.framework */; };
511+
C1F2075C26D6F9B0007AB7EB /* ProfileExpirationAlerter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F2075B26D6F9B0007AB7EB /* ProfileExpirationAlerter.swift */; };
511512
C1F8B243223E73FD00DD66CF /* BolusProgressTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F8B1D122375E4200DD66CF /* BolusProgressTableViewCell.swift */; };
512513
C1FB428C217806A400FAB378 /* StateColorPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FB428B217806A300FAB378 /* StateColorPalette.swift */; };
513514
C1FB428D21791D2500FAB378 /* PumpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C3B6F620BBCAA30026CAFA /* PumpManager.swift */; };
@@ -1418,6 +1419,7 @@
14181419
C1DE5D22251BFC4D00439E49 /* SimpleBolusView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleBolusView.swift; sourceTree = "<group>"; };
14191420
C1E2773D224177C000354103 /* ClockKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ClockKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/System/Library/Frameworks/ClockKit.framework; sourceTree = DEVELOPER_DIR; };
14201421
C1E2774722433D7A00354103 /* MKRingProgressView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MKRingProgressView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1422+
C1F2075B26D6F9B0007AB7EB /* ProfileExpirationAlerter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileExpirationAlerter.swift; sourceTree = "<group>"; };
14211423
C1F8B1D122375E4200DD66CF /* BolusProgressTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusProgressTableViewCell.swift; sourceTree = "<group>"; };
14221424
C1F8B1DB223862D500DD66CF /* BolusProgressTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BolusProgressTableViewCell.xib; sourceTree = "<group>"; };
14231425
C1FB428B217806A300FAB378 /* StateColorPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateColorPalette.swift; sourceTree = "<group>"; };
@@ -2097,6 +2099,7 @@
20972099
4328E0341CFC0AE100E199AA /* WatchDataManager.swift */,
20982100
1DA6499D2441266400F61E75 /* Alerts */,
20992101
E95D37FF24EADE68005E2F50 /* Store Protocols */,
2102+
C1F2075B26D6F9B0007AB7EB /* ProfileExpirationAlerter.swift */,
21002103
);
21012104
path = Managers;
21022105
sourceTree = "<group>";
@@ -3431,6 +3434,7 @@
34313434
C1D289B522F90A52003FFBD9 /* BasalDeliveryState.swift in Sources */,
34323435
4F2C15821E074FC600E160D4 /* NSTimeInterval.swift in Sources */,
34333436
4311FB9B1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift in Sources */,
3437+
C1F2075C26D6F9B0007AB7EB /* ProfileExpirationAlerter.swift in Sources */,
34343438
B4FEEF7D24B8A71F00A8DF9B /* DeviceDataManager+DeviceStatus.swift in Sources */,
34353439
E95D380324EADF36005E2F50 /* CarbStoreProtocol.swift in Sources */,
34363440
E98A55ED24EDD6380008715D /* SettingsStoreProtocol.swift in Sources */,

Loop/Managers/LoopAppManager.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ class LoopAppManager: NSObject {
204204
// MARK: - Life Cycle
205205

206206
func didBecomeActive() {
207+
if let rootViewController = rootViewController {
208+
ProfileExpirationAlerter.alertIfNeeded(viewControllerToPresentFrom: rootViewController)
209+
}
207210
deviceDataManager?.didBecomeActive()
208211
}
209212

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// ProfileExpirationAlerter.swift
3+
// Loop
4+
//
5+
// Created by Pete Schwamb on 8/21/21.
6+
// Copyright © 2021 LoopKit Authors. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import UserNotifications
11+
import LoopCore
12+
13+
14+
class ProfileExpirationAlerter {
15+
16+
static let expirationAlertWindow: TimeInterval = .days(20)
17+
18+
static func alertIfNeeded(viewControllerToPresentFrom: UIViewController) {
19+
20+
let now = Date()
21+
22+
guard let profileExpiration = Bundle.main.profileExpiration, now > profileExpiration - expirationAlertWindow else {
23+
return
24+
}
25+
26+
let timeUntilExpiration = profileExpiration.timeIntervalSince(now)
27+
28+
let minimumTimeBetweenAlerts: TimeInterval = timeUntilExpiration > .hours(24) ? .days(2) : .hours(1)
29+
30+
if let lastAlert = UserDefaults.appGroup?.lastProfileExpirationAlertDate {
31+
guard now > lastAlert + minimumTimeBetweenAlerts else {
32+
return
33+
}
34+
}
35+
36+
let formatter = DateComponentsFormatter()
37+
formatter.allowedUnits = [.day, .hour]
38+
formatter.unitsStyle = .full
39+
formatter.zeroFormattingBehavior = .dropLeading
40+
formatter.maximumUnitCount = 1
41+
let timeUntilExpirationStr = formatter.string(from: timeUntilExpiration)
42+
43+
let dialog = UIAlertController(
44+
title: NSLocalizedString("Profile Expires Soon", comment: "The title for notification of upcoming profile expiration"),
45+
message: String(format: NSLocalizedString("Loop will stop working in %@. You will need to update Loop before that, with a new provisioning profile.", comment: "Format string for body for notification of upcoming provisioning profile expiration. (1: amount of time until expiration"), timeUntilExpirationStr!),
46+
preferredStyle: .alert)
47+
dialog.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Text for ok action on notification of upcoming profile expiration"), style: .default, handler: nil))
48+
dialog.addAction(UIAlertAction(title: NSLocalizedString("More Info", comment: "Text for more info action on notification of upcoming profile expiration"), style: .default, handler: { (_) in
49+
UIApplication.shared.open(URL(string: "https://loopkit.github.io/loopdocs/build/updating/")!)
50+
}))
51+
viewControllerToPresentFrom.present(dialog, animated: true, completion: nil)
52+
53+
UserDefaults.appGroup?.lastProfileExpirationAlertDate = now
54+
}
55+
}

LoopCore/NSUserDefaults.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extension UserDefaults {
2323
case overrideHistory = "com.loopkit.overrideHistory"
2424
case lastBedtimeQuery = "com.loopkit.Loop.lastBedtimeQuery"
2525
case bedtime = "com.loopkit.Loop.bedtime"
26+
case lastProfileExpirationAlertDate = "com.loopkit.Loop.lastProfileExpirationAlertDate"
2627
}
2728

2829
public static let appGroup = UserDefaults(suiteName: Bundle.main.appGroupSuiteName)
@@ -66,7 +67,11 @@ extension UserDefaults {
6667
let modelRaw = rawValue["model"] as? ExponentialInsulinModelPreset.RawValue,
6768
let preset = ExponentialInsulinModelPreset(rawValue: modelRaw)
6869
{
69-
removeObject(forKey: Key.legacyInsulinModelSettings.rawValue)
70+
// Keep this for people switching between versions
71+
//removeObject(forKey: Key.legacyInsulinModelSettings.rawValue)
72+
73+
// Save migrated value
74+
set(preset.rawValue, forKey: Key.defaultRapidActingModel.rawValue)
7075
return preset
7176
}
7277
}
@@ -177,4 +182,17 @@ extension UserDefaults {
177182
set(newValue, forKey: Key.bedtime.rawValue)
178183
}
179184
}
185+
186+
public var lastProfileExpirationAlertDate: Date? {
187+
get {
188+
if let rawValue = object(forKey: Key.lastProfileExpirationAlertDate.rawValue) as? Date {
189+
return rawValue
190+
} else {
191+
return nil
192+
}
193+
}
194+
set {
195+
set(newValue, forKey: Key.lastProfileExpirationAlertDate.rawValue)
196+
}
197+
}
180198
}

0 commit comments

Comments
 (0)