Skip to content

Commit c7314b7

Browse files
authored
Merge pull request #296 from loopandlearn/notifications
Notification of new version, blacklisted version and build expiration
2 parents 873f1bd + 4c47679 commit c7314b7

File tree

5 files changed

+98
-19
lines changed

5 files changed

+98
-19
lines changed

LoopFollow/Application/AppDelegate.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
4242
return true
4343
}
4444

45-
4645
func applicationWillTerminate(_ application: UIApplication) {
4746
if UserDefaultsRepository.alertAppInactive.value {
4847
AlarmSound.setSoundFile(str: "Alarm_Buzzer")

LoopFollow/ViewControllers/MainViewController.swift

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,9 +423,63 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
423423
}
424424

425425
restartAllTimers()
426-
426+
checkAndNotifyVersionStatus()
427+
checkAppExpirationStatus()
427428
}
428429

430+
func checkAndNotifyVersionStatus() {
431+
let versionManager = AppVersionManager()
432+
versionManager.checkForNewVersion { latestVersion, isNewer, isBlacklisted in
433+
let now = Date()
434+
435+
// Check if the current version is blacklisted, or if there is a newer version available
436+
if isBlacklisted {
437+
let lastBlacklistShown = UserDefaultsRepository.lastBlacklistNotificationShown.value ?? Date.distantPast
438+
if now.timeIntervalSince(lastBlacklistShown) > 86400 { // 24 hours
439+
self.versionAlert(message: "The current version has a critical issue and should be updated as soon as possible.")
440+
UserDefaultsRepository.lastBlacklistNotificationShown.value = now
441+
UserDefaultsRepository.lastVersionUpdateNotificationShown.value = now
442+
}
443+
} else if isNewer {
444+
let lastVersionUpdateShown = UserDefaultsRepository.lastVersionUpdateNotificationShown.value ?? Date.distantPast
445+
if now.timeIntervalSince(lastVersionUpdateShown) > 1209600 { // 2 weeks
446+
self.versionAlert(message: "A new version is available: \(latestVersion ?? "Unknown"). It is recommended to update.")
447+
UserDefaultsRepository.lastVersionUpdateNotificationShown.value = now
448+
}
449+
}
450+
}
451+
}
452+
453+
func versionAlert(title: String = "Update Available", message: String) {
454+
DispatchQueue.main.async {
455+
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
456+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
457+
self.present(alert, animated: true)
458+
}
459+
}
460+
461+
func checkAppExpirationStatus() {
462+
let now = Date()
463+
let expirationDate = BuildDetails.default.calculateExpirationDate()
464+
let weekBeforeExpiration = Calendar.current.date(byAdding: .day, value: -7, to: expirationDate)!
465+
466+
if now >= weekBeforeExpiration {
467+
let lastExpirationShown = UserDefaultsRepository.lastExpirationNotificationShown.value ?? Date.distantPast
468+
if now.timeIntervalSince(lastExpirationShown) > 86400 { // 24 hours
469+
expirationAlert()
470+
UserDefaultsRepository.lastExpirationNotificationShown.value = now
471+
}
472+
}
473+
}
474+
475+
func expirationAlert() {
476+
DispatchQueue.main.async {
477+
let alert = UIAlertController(title: "App Expiration Warning", message: "This app will expire in less than a week. Please rebuild to continue using it.", preferredStyle: .alert)
478+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
479+
self.present(alert, animated: true)
480+
}
481+
}
482+
429483
@objc override func viewDidAppear(_ animated: Bool) {
430484
showHideNSDetails()
431485
}

LoopFollow/helpers/AppVersionManager.swift

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,38 @@ import Foundation
1111
class AppVersionManager {
1212
private let githubService = GitHubService()
1313

14+
/// Checks for the availability of a new app version and if the current version is blacklisted.
15+
/// - Parameter completion: Returns latest version, a boolean for newer version existence, and blacklist status.
16+
/// Usage: `versionManager.checkForNewVersion { latestVersion, isNewer, isBlacklisted in ... }`
1417
func checkForNewVersion(completion: @escaping (String?, Bool, Bool) -> Void) {
1518
let currentVersion = version()
1619
let now = Date()
17-
18-
// Retrieve cache
19-
let lastChecked = UserDefaults.standard.object(forKey: "latestVersionChecked") as? Date ?? Date.distantPast
20-
let cachedLatestVersion = UserDefaults.standard.string(forKey: "latestVersion")
21-
let isBlacklistedCached = UserDefaults.standard.bool(forKey: "isCurrentVersionBlacklisted")
2220

21+
// Retrieve cache
22+
let latestVersionChecked = UserDefaultsRepository.latestVersionChecked.value ?? Date.distantPast
23+
let latestVersion = UserDefaultsRepository.latestVersion.value
24+
let currentVersionBlackListed = UserDefaultsRepository.currentVersionBlackListed.value
25+
let cachedForVersion = UserDefaultsRepository.cachedForVersion.value
26+
27+
// Reset notifications if version has changed
28+
if let cachedVersion = cachedForVersion, cachedVersion != currentVersion {
29+
UserDefaultsRepository.lastBlacklistNotificationShown.value = Date.distantPast
30+
UserDefaultsRepository.lastVersionUpdateNotificationShown.value = Date.distantPast
31+
}
32+
2333
// Check if the cache is still valid
24-
if now.timeIntervalSince(lastChecked) < 24 * 3600, let latestVersion = cachedLatestVersion {
34+
if let cachedVersion = cachedForVersion, cachedVersion == currentVersion,
35+
now.timeIntervalSince(latestVersionChecked) < 24 * 3600, let latestVersion = latestVersion {
2536
let isNewer = isVersion(latestVersion, newerThan: currentVersion)
26-
completion(latestVersion, isNewer, isBlacklistedCached)
37+
completion(latestVersion, isNewer, currentVersionBlackListed)
2738
return
2839
}
29-
30-
// Fetch new data if cache is outdated
40+
41+
// Fetch new data if cache is outdated or not for current version
42+
fetchDataAndUpdateCache(currentVersion: currentVersion, completion: completion)
43+
}
44+
45+
private func fetchDataAndUpdateCache(currentVersion: String, completion: @escaping (String?, Bool, Bool) -> Void) {
3146
githubService.fetchData(for: .versionConfig) { versionData in
3247
self.githubService.fetchData(for: .blacklistedVersions) { blacklistData in
3348
DispatchQueue.main.async {
@@ -39,9 +54,10 @@ class AppVersionManager {
3954
.map { $0.blacklistedVersions.map { $0.version }.contains(currentVersion) } ?? false
4055

4156
// Update cache with new data
42-
UserDefaults.standard.set(fetchedVersion, forKey: "latestVersion")
43-
UserDefaults.standard.set(Date(), forKey: "latestVersionChecked")
44-
UserDefaults.standard.set(isBlacklisted, forKey: "isCurrentVersionBlacklisted")
57+
UserDefaultsRepository.latestVersion.value = fetchedVersion
58+
UserDefaultsRepository.latestVersionChecked.value = Date()
59+
UserDefaultsRepository.currentVersionBlackListed.value = isBlacklisted
60+
UserDefaultsRepository.cachedForVersion.value = currentVersion
4561

4662
// Call completion with new data
4763
completion(fetchedVersion, isNewer, isBlacklisted)
@@ -62,7 +78,7 @@ class AppVersionManager {
6278
}
6379
return nil
6480
}
65-
81+
6682
private func isVersion(_ fetchedVersion: String, newerThan currentVersion: String) -> Bool {
6783
let fetchedVersionComponents = fetchedVersion.split(separator: ".").map { Int($0) ?? 0 }
6884
let currentVersionComponents = currentVersion.split(separator: ".").map { Int($0) ?? 0 }
@@ -79,18 +95,18 @@ class AppVersionManager {
7995
}
8096
return false
8197
}
82-
98+
8399
func version() -> String {
84100
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
85101
return version
86102
}
87103
return "Unknown"
88104
}
89-
105+
90106
struct Blacklist: Decodable {
91107
let blacklistedVersions: [VersionEntry]
92108
}
93-
109+
94110
struct VersionEntry: Decodable {
95111
let version: String
96112
}

LoopFollow/helpers/BuildDetails.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class BuildDetails {
7171
if let provision = MobileProvision.read() {
7272
return provision.expirationDate
7373
} else {
74-
return Date()
74+
return .distantFuture
7575
}
7676
}
7777
}

LoopFollow/repository/UserDefaults.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,20 @@ class UserDefaultsRepository {
460460
static let alertRecBolusSnoozedTime = UserDefaultsValue<Date?>(key: "alertRecBolusSnoozedTime", default: nil)
461461
static var deviceRecBolus: UserDefaultsValue<Double> = UserDefaultsValue(key: "deviceRecBolus", default: 0.0)
462462

463+
//What version is the cache valid for
464+
static let cachedForVersion = UserDefaultsValue<String?>(key: "cachedForVersion", default: nil)
465+
463466
//Caching of latest version
464467
static let latestVersion = UserDefaultsValue<String?>(key: "latestVersion", default: nil)
465468
static let latestVersionChecked = UserDefaultsValue<Date?>(key: "latestVersionChecked", default: nil)
466469

467470
//Caching of blacklisted version
468471
static let currentVersionBlackListed = UserDefaultsValue<Bool>(key: "currentVersionBlackListed", default: false)
472+
473+
// Tracking notifications to manage frequency
474+
static let lastBlacklistNotificationShown = UserDefaultsValue<Date?>(key: "lastBlacklistNotificationShown", default: nil)
475+
static let lastVersionUpdateNotificationShown = UserDefaultsValue<Date?>(key: "lastVersionUpdateNotificationShown", default: nil)
476+
477+
// Tracking the last time the expiration notification was shown
478+
static let lastExpirationNotificationShown = UserDefaultsValue<Date?>(key: "lastExpirationNotificationShown", default: nil)
469479
}

0 commit comments

Comments
 (0)