@@ -47,6 +47,51 @@ class LoopAPNSService {
4747 }
4848 }
4949
50+ private func createReturnNotificationInfo( ) -> [ String : Any ] ? {
51+ let loopFollowDeviceToken = Observable . shared. loopFollowDeviceToken. value
52+ guard !loopFollowDeviceToken. isEmpty else { return nil }
53+
54+ // Get LoopFollow's own Team ID from BuildDetails.
55+ guard let loopFollowTeamID = BuildDetails . default. teamID, !loopFollowTeamID. isEmpty else {
56+ LogManager . shared. log ( category: . apns, message: " LoopFollow Team ID not found in BuildDetails.plist. Cannot create return notification info. " )
57+ return nil
58+ }
59+
60+ // Get the target Loop app's Team ID from storage.
61+ let targetTeamId = storage. teamId. value ?? " "
62+ let teamIdsAreDifferent = loopFollowTeamID != targetTeamId
63+
64+ let keyIdForReturn : String
65+ let apnsKeyForReturn : String
66+
67+ if teamIdsAreDifferent {
68+ // Team IDs differ, use the separate return credentials.
69+ keyIdForReturn = storage. returnKeyId. value
70+ apnsKeyForReturn = storage. returnApnsKey. value
71+ } else {
72+ // Team IDs are the same, use the primary credentials.
73+ keyIdForReturn = storage. keyId. value
74+ apnsKeyForReturn = storage. apnsKey. value
75+ }
76+
77+ // Ensure we have the necessary credentials.
78+ guard !keyIdForReturn. isEmpty, !apnsKeyForReturn. isEmpty else {
79+ LogManager . shared. log ( category: . apns, message: " Missing required return APNS credentials. Check Remote Settings. " )
80+ return nil
81+ }
82+
83+ let returnInfo : [ String : Any ] = [
84+ " production_environment " : BuildDetails . default. isTestFlightBuild ( ) ,
85+ " device_token " : loopFollowDeviceToken,
86+ " bundle_id " : Bundle . main. bundleIdentifier ?? " " ,
87+ " team_id " : loopFollowTeamID,
88+ " key_id " : keyIdForReturn,
89+ " apns_key " : apnsKeyForReturn,
90+ ]
91+
92+ return returnInfo
93+ }
94+
5095 /// Validates the Loop APNS setup by checking all required fields
5196 /// - Returns: True if setup is valid, false otherwise
5297 func validateSetup( ) -> Bool {
@@ -92,7 +137,7 @@ class LoopAPNSService {
92137 let carbsAmount = payload. carbsAmount ?? 0.0
93138 let absorptionTime = payload. absorptionTime ?? 3.0
94139 let startTime = payload. consumedDate ?? now
95- let finalPayload = [
140+ var finalPayload = [
96141 " carbs-entry " : carbsAmount,
97142 " absorption-time " : absorptionTime,
98143 " otp " : String ( payload. otp) ,
@@ -105,6 +150,16 @@ class LoopAPNSService {
105150 " alert " : " Remote Carbs Entry: \( String ( format: " %.1f " , carbsAmount) ) grams \n Absorption Time: \( String ( format: " %.1f " , absorptionTime) ) hours " ,
106151 ] as [ String : Any ]
107152
153+ /* Let's wait with this until we have an encryption solution for LRC
154+ if let returnInfo = createReturnNotificationInfo() {
155+ finalPayload["return_notification"] = returnInfo
156+ }
157+ */
158+
159+ // Log the exact carbs amount for debugging precision issues
160+ LogManager . shared. log ( category: . apns, message: " Carbs amount - Raw: \( payload. carbsAmount ?? 0.0 ) , Formatted: \( String ( format: " %.1f " , carbsAmount) ) , JSON: \( carbsAmount) " )
161+ LogManager . shared. log ( category: . apns, message: " Absorption time - Raw: \( payload. absorptionTime ?? 3.0 ) , Formatted: \( String ( format: " %.1f " , absorptionTime) ) , JSON: \( absorptionTime) " )
162+
108163 // Log carbs entry attempt
109164 LogManager . shared. log ( category: . apns, message: " Sending carbs: \( String ( format: " %.1f " , carbsAmount) ) g, absorption: \( String ( format: " %.1f " , absorptionTime) ) h " )
110165
@@ -142,7 +197,7 @@ class LoopAPNSService {
142197 // Create the complete notification payload (matching Nightscout's exact format)
143198 // Based on Nightscout's loop.js implementation
144199 let bolusAmount = payload. bolusAmount ?? 0.0
145- let finalPayload = [
200+ var finalPayload = [
146201 " bolus-entry " : bolusAmount,
147202 " otp " : String ( payload. otp) ,
148203 " remote-address " : " LoopFollow " ,
@@ -153,6 +208,15 @@ class LoopAPNSService {
153208 " alert " : " Remote Bolus Entry: \( String ( format: " %.2f " , bolusAmount) ) U " ,
154209 ] as [ String : Any ]
155210
211+ /* Let's wait with this until we have an encryption solution for LRC
212+ if let returnInfo = createReturnNotificationInfo() {
213+ finalPayload["return_notification"] = returnInfo
214+ }
215+ */
216+
217+ // Log the exact bolus amount for debugging precision issues
218+ LogManager . shared. log ( category: . apns, message: " Bolus amount - Raw: \( payload. bolusAmount ?? 0.0 ) , Formatted: \( String ( format: " %.2f " , bolusAmount) ) , JSON: \( bolusAmount) " )
219+
156220 // Log bolus entry attempt
157221 LogManager . shared. log ( category: . apns, message: " Sending bolus: \( String ( format: " %.2f " , bolusAmount) ) U " )
158222
@@ -587,6 +651,10 @@ class LoopAPNSService {
587651 payload [ " override-duration-minutes " ] = Int ( duration / 60 )
588652 }
589653
654+ if let returnInfo = createReturnNotificationInfo ( ) {
655+ payload [ " return_notification " ] = returnInfo
656+ }
657+
590658 // Send the notification using the existing APNS infrastructure
591659 sendAPNSNotification (
592660 deviceToken: deviceToken,
@@ -619,7 +687,7 @@ class LoopAPNSService {
619687 let now = Date ( )
620688 let expiration = Date ( timeIntervalSinceNow: 5 * 60 ) // 5 minutes from now
621689
622- let payload : [ String : Any ] = [
690+ var payload : [ String : Any ] = [
623691 " cancel-temporary-override " : " true " ,
624692 " remote-address " : " LoopFollow " ,
625693 " entered-by " : " LoopFollow " ,
@@ -628,6 +696,10 @@ class LoopAPNSService {
628696 " alert " : " Cancel Temporary Override " ,
629697 ]
630698
699+ if let returnInfo = createReturnNotificationInfo ( ) {
700+ payload [ " return_notification " ] = returnInfo
701+ }
702+
631703 // Send the notification using the existing APNS infrastructure
632704 sendAPNSNotification (
633705 deviceToken: deviceToken,
0 commit comments