Skip to content

Commit b0fb04f

Browse files
committed
Improved PromoViews. Better APNS
1 parent af0a725 commit b0fb04f

16 files changed

Lines changed: 278 additions & 88 deletions

File tree

nightguard WatchKit App/ExtensionDelegate.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class ExtensionDelegate: NSObject, WKApplicationDelegate {
6969

7070
WatchMessageService.singleton.onMessage { (message: NightscoutDataMessage) in
7171
NightscoutDataRepository.singleton.storeCurrentNightscoutData(message.nightscoutData)
72+
let displaySnapshot = message.displaySnapshot ?? NightguardDisplaySnapshot.make(from: message.nightscoutData)
73+
NightscoutDataRepository.singleton.storeLatestDisplaySnapshot(displaySnapshot)
74+
WidgetCenter.shared.reloadTimelines(ofKind: "org.duckdns.dhe.nightguard.NightguardDefaultWidgets")
75+
WidgetCenter.shared.reloadTimelines(ofKind: "org.duckdns.dhe.nightguard.NightguardTimestampWidgets")
76+
WidgetCenter.shared.reloadTimelines(ofKind: "org.duckdns.dhe.nightguard.NightguardGaugeWidgets")
7277
MainController.mainViewModel.pushBackgroundData(newNightscoutData: message.nightscoutData)
7378
}
7479

nightguard WatchKit App/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<key>CFBundleSignature</key>
2222
<string>????</string>
2323
<key>CFBundleVersion</key>
24-
<string>1001</string>
24+
<string>1002</string>
2525
<key>NSAppTransportSecurity</key>
2626
<dict>
2727
<key>NSAllowsArbitraryLoads</key>

nightguard Widget Extension/NightguardTimelineProvider.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ struct NightguardTimelineProvider: TimelineProvider {
6161
private func getTimelineData(completion: @escaping (NightscoutDataEntry) -> Void) {
6262

6363
BackgroundRefreshLogger.info("TimelineProvider is getting Timeline...")
64+
if let snapshot = NightscoutDataRepository.singleton.loadLatestDisplaySnapshot(),
65+
snapshot.isFresh() {
66+
BackgroundRefreshLogger.info("TimelineProvider is using latest display snapshot...")
67+
completion(NightscoutDataEntry(snapshot: snapshot))
68+
return
69+
}
70+
6471
let oldData = NightscoutDataRepository.singleton.loadCurrentNightscoutData()
6572
let oldEntries = NightscoutDataRepository.singleton.loadTodaysBgData()
6673

nightguard Widget Extension/NightscoutDataEntry.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,51 @@ struct NightscoutDataEntry: TimelineEntry {
8585
configuration: ConfigurationIntent())
8686
}
8787

88+
extension NightscoutDataEntry {
89+
init(snapshot: NightguardDisplaySnapshot) {
90+
self.init(
91+
date: snapshot.date,
92+
sgv: snapshot.sgv,
93+
sgvColor: UIColor(
94+
red: CGFloat(snapshot.sgvColorRed),
95+
green: CGFloat(snapshot.sgvColorGreen),
96+
blue: CGFloat(snapshot.sgvColorBlue),
97+
alpha: 1
98+
),
99+
bgdeltaString: snapshot.bgdeltaString,
100+
bgdeltaColor: UIColor(
101+
red: CGFloat(snapshot.bgdeltaColorRed),
102+
green: CGFloat(snapshot.bgdeltaColorGreen),
103+
blue: CGFloat(snapshot.bgdeltaColorBlue),
104+
alpha: 1
105+
),
106+
bgdeltaArrow: snapshot.bgdeltaArrow,
107+
bgdelta: Float(snapshot.bgdelta),
108+
time: NSNumber(value: snapshot.timestamp),
109+
battery: snapshot.battery,
110+
iob: snapshot.iob,
111+
cob: snapshot.cob,
112+
snoozedUntilTimestamp: snapshot.snoozedUntilTimestamp,
113+
lastBGValues: [
114+
BgEntry(
115+
value: snapshot.sgv,
116+
valueColor: UIColor(
117+
red: CGFloat(snapshot.sgvColorRed),
118+
green: CGFloat(snapshot.sgvColorGreen),
119+
blue: CGFloat(snapshot.sgvColorBlue),
120+
alpha: 1
121+
),
122+
delta: snapshot.bgdeltaString,
123+
timestamp: snapshot.timestamp,
124+
arrow: snapshot.bgdeltaArrow
125+
)
126+
],
127+
errorMessage: "",
128+
configuration: ConfigurationIntent()
129+
)
130+
}
131+
}
132+
88133
struct BgEntry : Identifiable, Hashable {
89134

90135
let id = UUID()

nightguard.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,7 +2222,7 @@
22222222
CODE_SIGN_ENTITLEMENTS = "nightguard WatchKit App/nightguard.entitlements";
22232223
CODE_SIGN_IDENTITY = "iPhone Developer";
22242224
CODE_SIGN_STYLE = Automatic;
2225-
CURRENT_PROJECT_VERSION = 1001;
2225+
CURRENT_PROJECT_VERSION = 1002;
22262226
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
22272227
INFOPLIST_FILE = "nightguard WatchKit App/Info.plist";
22282228
LD_RUNPATH_SEARCH_PATHS = (
@@ -2252,7 +2252,7 @@
22522252
CODE_SIGN_ENTITLEMENTS = "nightguard WatchKit App/nightguard.entitlements";
22532253
CODE_SIGN_IDENTITY = "iPhone Developer";
22542254
CODE_SIGN_STYLE = Automatic;
2255-
CURRENT_PROJECT_VERSION = 1001;
2255+
CURRENT_PROJECT_VERSION = 1002;
22562256
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
22572257
INFOPLIST_FILE = "nightguard WatchKit App/Info.plist";
22582258
LD_RUNPATH_SEARCH_PATHS = (
@@ -2279,7 +2279,7 @@
22792279
CODE_SIGN_ENTITLEMENTS = nightguard/scoutwatch.entitlements;
22802280
CODE_SIGN_IDENTITY = "iPhone Developer";
22812281
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
2282-
CURRENT_PROJECT_VERSION = 1001;
2282+
CURRENT_PROJECT_VERSION = 1002;
22832283
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
22842284
ENABLE_BITCODE = NO;
22852285
EXCLUDED_ARCHS = "";
@@ -2308,7 +2308,7 @@
23082308
CODE_SIGN_ENTITLEMENTS = nightguard/scoutwatch.entitlements;
23092309
CODE_SIGN_IDENTITY = "iPhone Developer";
23102310
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
2311-
CURRENT_PROJECT_VERSION = 1001;
2311+
CURRENT_PROJECT_VERSION = 1002;
23122312
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
23132313
ENABLE_BITCODE = NO;
23142314
INFOPLIST_FILE = nightguard/Info.plist;
@@ -2421,7 +2421,7 @@
24212421
CODE_SIGN_IDENTITY = "iPhone Developer";
24222422
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
24232423
CODE_SIGN_STYLE = Automatic;
2424-
CURRENT_PROJECT_VERSION = 1001;
2424+
CURRENT_PROJECT_VERSION = 1002;
24252425
GCC_C_LANGUAGE_STANDARD = gnu11;
24262426
GENERATE_INFOPLIST_FILE = YES;
24272427
INFOPLIST_FILE = "nightguard Widget Extension/Info.plist";
@@ -2461,7 +2461,7 @@
24612461
CODE_SIGN_IDENTITY = "iPhone Developer";
24622462
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
24632463
CODE_SIGN_STYLE = Automatic;
2464-
CURRENT_PROJECT_VERSION = 1001;
2464+
CURRENT_PROJECT_VERSION = 1002;
24652465
GCC_C_LANGUAGE_STANDARD = gnu11;
24662466
GENERATE_INFOPLIST_FILE = YES;
24672467
INFOPLIST_FILE = "nightguard Widget Extension/Info.plist";
@@ -2498,7 +2498,7 @@
24982498
CODE_SIGN_ENTITLEMENTS = "nightguard ComplicationExtension.entitlements";
24992499
CODE_SIGN_IDENTITY = "Apple Development";
25002500
CODE_SIGN_STYLE = Automatic;
2501-
CURRENT_PROJECT_VERSION = 1001;
2501+
CURRENT_PROJECT_VERSION = 1002;
25022502
GCC_C_LANGUAGE_STANDARD = gnu11;
25032503
GENERATE_INFOPLIST_FILE = YES;
25042504
INFOPLIST_FILE = "nightguard Complication/Info.plist";
@@ -2540,7 +2540,7 @@
25402540
CODE_SIGN_ENTITLEMENTS = "nightguard ComplicationExtension.entitlements";
25412541
CODE_SIGN_IDENTITY = "Apple Development";
25422542
CODE_SIGN_STYLE = Automatic;
2543-
CURRENT_PROJECT_VERSION = 1001;
2543+
CURRENT_PROJECT_VERSION = 1002;
25442544
GCC_C_LANGUAGE_STANDARD = gnu11;
25452545
GENERATE_INFOPLIST_FILE = YES;
25462546
INFOPLIST_FILE = "nightguard Complication/Info.plist";

nightguard/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<key>CFBundleSignature</key>
2626
<string>????</string>
2727
<key>CFBundleVersion</key>
28-
<string>1001</string>
28+
<string>1002</string>
2929
<key>ITSAppUsesNonExemptEncryption</key>
3030
<false/>
3131
<key>LSApplicationCategoryType</key>

nightguard/app/BackgroundUpdateCoordinator.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ final class BackgroundUpdateCoordinator {
6969
category: .backgroundUpdates
7070
)
7171

72+
let snapshot = NightscoutDataRepository.singleton.storeLatestDisplaySnapshot(from: nightscoutData)
73+
AppLogger.singleton.debug(
74+
"\(trigger.rawValue) stored display snapshot: SGV=\(snapshot.sgv), timestamp=\(snapshot.timestamp)",
75+
category: .backgroundUpdates
76+
)
77+
7278
AlarmNotificationService.singleton.notifyIfAlarmActivated(nightscoutData)
7379
WatchService.singleton.sendToWatchCurrentNightwatchData()
7480

nightguard/app/LiveActivityManager.swift

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class LiveActivityManager {
112112
)
113113
}
114114

115-
let contentState = makeContentState(from: nightscoutData)
115+
let contentState = makeContentState(from: NightguardDisplaySnapshot.make(from: nightscoutData))
116116
var updatedActivityCount = 0
117117
for activity in activities {
118118
if #available(iOS 16.2, *) {
@@ -140,6 +140,7 @@ class LiveActivityManager {
140140

141141
func update(with nightscoutData: NightscoutData) {
142142
#if os(iOS)
143+
NightscoutDataRepository.singleton.storeLatestDisplaySnapshot(from: nightscoutData)
143144
let displayValues = makeDisplayValues(from: nightscoutData)
144145

145146
startOrUpdateActivity(
@@ -170,16 +171,22 @@ class LiveActivityManager {
170171
#if canImport(ActivityKit)
171172
@available(iOS 16.1, *)
172173
private func makeContentState(from nightscoutData: NightscoutData) -> NightguardActivityAttributes.ContentState {
173-
let displayValues = makeDisplayValues(from: nightscoutData)
174-
return makeContentState(
175-
sgv: displayValues.sgv,
176-
delta: displayValues.delta,
177-
trendArrow: displayValues.trendArrow,
178-
date: displayValues.date,
179-
bgDelta: displayValues.bgDelta,
180-
sgvColor: displayValues.sgvColor,
181-
iob: displayValues.iob,
182-
cob: displayValues.cob
174+
makeContentState(from: NightguardDisplaySnapshot.make(from: nightscoutData))
175+
}
176+
177+
@available(iOS 16.1, *)
178+
private func makeContentState(from snapshot: NightguardDisplaySnapshot) -> NightguardActivityAttributes.ContentState {
179+
NightguardActivityAttributes.ContentState(
180+
sgv: snapshot.sgv,
181+
delta: snapshot.bgdeltaString,
182+
trendArrow: snapshot.bgdeltaArrow,
183+
date: snapshot.date,
184+
bgDelta: snapshot.bgdelta,
185+
sgvColorRed: snapshot.sgvColorRed,
186+
sgvColorGreen: snapshot.sgvColorGreen,
187+
sgvColorBlue: snapshot.sgvColorBlue,
188+
iob: snapshot.iob,
189+
cob: snapshot.cob
183190
)
184191
}
185192

nightguard/repository/NightscoutDataRepository.swift

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,77 @@
77
//
88

99
import Foundation
10+
import UIKit
1011
#if canImport(WidgetKit)
1112
import WidgetKit
1213
#endif
1314

15+
struct NightguardDisplaySnapshot: Codable, Hashable {
16+
static let maxFreshAge: TimeInterval = 15 * 60
17+
18+
let sgv: String
19+
let bgdeltaString: String
20+
let bgdeltaArrow: String
21+
let bgdelta: Double
22+
let timestamp: TimeInterval
23+
let battery: String
24+
let iob: String
25+
let cob: String
26+
let snoozedUntilTimestamp: TimeInterval
27+
let sgvColorRed: Double
28+
let sgvColorGreen: Double
29+
let sgvColorBlue: Double
30+
let bgdeltaColorRed: Double
31+
let bgdeltaColorGreen: Double
32+
let bgdeltaColorBlue: Double
33+
let createdAt: Date
34+
35+
var date: Date {
36+
Date(timeIntervalSince1970: timestamp / 1000)
37+
}
38+
39+
func isFresh(referenceDate: Date = Date(), maxAge: TimeInterval = maxFreshAge) -> Bool {
40+
referenceDate.timeIntervalSince(date) <= maxAge
41+
}
42+
43+
static func make(from data: NightscoutData) -> NightguardDisplaySnapshot {
44+
let displaySgv = UnitsConverter.mgdlToDisplayUnits(data.sgv)
45+
let displayDelta = UnitsConverter.mgdlToDisplayUnitsWithSign("\(data.bgdelta)")
46+
let sgvColor = UIColorChanger.getBgColor(displaySgv)
47+
let bgdeltaColor = UIColorChanger.getDeltaLabelColor(data.bgdelta)
48+
let sgvComponents = colorComponents(from: sgvColor)
49+
let bgdeltaComponents = colorComponents(from: bgdeltaColor)
50+
51+
return NightguardDisplaySnapshot(
52+
sgv: displaySgv,
53+
bgdeltaString: displayDelta,
54+
bgdeltaArrow: data.bgdeltaArrow,
55+
bgdelta: Double(data.bgdelta),
56+
timestamp: data.time.doubleValue,
57+
battery: data.battery,
58+
iob: data.iob,
59+
cob: data.cob,
60+
snoozedUntilTimestamp: AlarmRule.snoozedUntilTimestamp.value,
61+
sgvColorRed: sgvComponents.red,
62+
sgvColorGreen: sgvComponents.green,
63+
sgvColorBlue: sgvComponents.blue,
64+
bgdeltaColorRed: bgdeltaComponents.red,
65+
bgdeltaColorGreen: bgdeltaComponents.green,
66+
bgdeltaColorBlue: bgdeltaComponents.blue,
67+
createdAt: Date()
68+
)
69+
}
70+
71+
private static func colorComponents(from color: UIColor) -> (red: Double, green: Double, blue: Double) {
72+
var red: CGFloat = 1
73+
var green: CGFloat = 1
74+
var blue: CGFloat = 1
75+
var alpha: CGFloat = 1
76+
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
77+
return (Double(red), Double(green), Double(blue))
78+
}
79+
}
80+
1481
// Repository to store BgData using the NSUserDefaults
1582
class NightscoutDataRepository {
1683

@@ -27,6 +94,7 @@ class NightscoutDataRepository {
2794
static let batteryChangeTime = "batteryChangeTime"
2895
static let deviceStatus = "deviceStatus"
2996
static let temporaryTarget = "temporaryTarget"
97+
static let latestDisplaySnapshot = "latestDisplaySnapshot"
3098
}
3199

32100
var isEmpty: Bool {
@@ -71,6 +139,28 @@ class NightscoutDataRepository {
71139
}
72140
return nightscoutData
73141
}
142+
143+
func storeLatestDisplaySnapshot(_ snapshot: NightguardDisplaySnapshot) {
144+
let defaults = UserDefaults(suiteName: AppConstants.APP_GROUP_ID)
145+
let data = try? JSONEncoder().encode(snapshot)
146+
defaults?.set(data, forKey: Constants.latestDisplaySnapshot)
147+
}
148+
149+
@discardableResult
150+
func storeLatestDisplaySnapshot(from data: NightscoutData) -> NightguardDisplaySnapshot {
151+
let snapshot = NightguardDisplaySnapshot.make(from: data)
152+
storeLatestDisplaySnapshot(snapshot)
153+
return snapshot
154+
}
155+
156+
func loadLatestDisplaySnapshot() -> NightguardDisplaySnapshot? {
157+
guard let defaults = UserDefaults(suiteName: AppConstants.APP_GROUP_ID),
158+
let data = defaults.object(forKey: Constants.latestDisplaySnapshot) as? Data else {
159+
return nil
160+
}
161+
162+
return try? JSONDecoder().decode(NightguardDisplaySnapshot.self, from: data)
163+
}
74164

75165
func storeTodaysBgData(_ todaysBgData : [BloodSugar]) {
76166

nightguard/resources/Base.lproj/Localizable.strings

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,10 +643,12 @@
643643
"Pro" = "Pro";
644644
"Pro unlocks the core premium features. For push-based background updates, choose Max." = "Pro unlocks the core premium features. For push-based background updates, choose Max.";
645645
"Max includes Pro and adds the background update features below." = "Max includes Pro and adds the background update features below.";
646+
"All Pro Features" = "All Pro Features";
647+
"Includes every feature from the Pro subscription." = "Includes every feature from the Pro subscription.";
646648
"Dynamic Island and Live Activity Updates" = "Dynamic Island and Live Activity Updates";
647649
"Updating Dynamic Island and Live Activities in the background is only possible through push notifications in Max. This is an Apple limitation." = "Updating Dynamic Island and Live Activities in the background is only possible through push notifications in Max. This is an Apple limitation.";
648650
"Why Max costs more" = "Why Max costs more";
649-
"Every Max user receives thousands of push notifications per month. This requires a hosted server backend, which is why the higher price is unfortunately necessary." = "Every Max user receives thousands of push notifications per month. This requires a hosted server backend, which is why the higher price is unfortunately necessary.";
651+
"Every Max user receives thousands of push notifications per month. We use the subscription revenue to finance the required hosted server backend." = "Every Max user receives thousands of push notifications per month. We use the subscription revenue to finance the required hosted server backend.";
650652
"When upgrading from Pro to Max, Apple automatically accounts for your remaining Pro subscription time in the purchase dialog." = "When upgrading from Pro to Max, Apple automatically accounts for your remaining Pro subscription time in the purchase dialog.";
651653

652654
/* App Tour */

0 commit comments

Comments
 (0)