Skip to content

Commit 01ff92f

Browse files
committed
Add request for Receive Receipts
* Let's arbitrarily keep receive requests up to 30 days. If these requests can't be sent for 30 days, let's drop them. Typically, they will be removed from cache when the request is successful, or fail with un-retryable error.
1 parent ce432f5 commit 01ff92f

File tree

4 files changed

+119
-1
lines changed

4 files changed

+119
-1
lines changed

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@
201201
3CFA8F572E9087DB00201FE5 /* OSRequestRemoveUpdateToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFA8F452E9087DB00201FE5 /* OSRequestRemoveUpdateToken.swift */; };
202202
3CFA8F582E9087DB00201FE5 /* OSLiveActivityRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFA8F432E9087DB00201FE5 /* OSLiveActivityRequest.swift */; };
203203
3CFA8F592E9087DB00201FE5 /* OneSignalLiveActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFA8F4C2E9087DB00201FE5 /* OneSignalLiveActivityAttributes.swift */; };
204+
3CFA8F5B2E9091A200201FE5 /* OSRequestLiveActivityReceiveReceipts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFA8F5A2E9091A200201FE5 /* OSRequestLiveActivityReceiveReceipts.swift */; };
204205
3E464ED71D88ED1F00DCF7E9 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37E6B2BA19D9CAF300D0C601 /* UIKit.framework */; };
205206
3E66F5821D90A2C600E45A01 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E08E2701D49A5C8002176DE /* SystemConfiguration.framework */; };
206207
4529DED21FA81EA800CEAB1D /* NSObjectOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DED11FA81EA800CEAB1D /* NSObjectOverrider.m */; };
@@ -1350,6 +1351,7 @@
13501351
3CFA8F4B2E9087DB00201FE5 /* OneSignalLiveActivitiesManagerImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneSignalLiveActivitiesManagerImpl.swift; sourceTree = "<group>"; };
13511352
3CFA8F4C2E9087DB00201FE5 /* OneSignalLiveActivityAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneSignalLiveActivityAttributes.swift; sourceTree = "<group>"; };
13521353
3CFA8F4D2E9087DB00201FE5 /* OSLiveActivitiesExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLiveActivitiesExtension.swift; sourceTree = "<group>"; };
1354+
3CFA8F5A2E9091A200201FE5 /* OSRequestLiveActivityReceiveReceipts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSRequestLiveActivityReceiveReceipts.swift; sourceTree = "<group>"; };
13531355
3E08E2701D49A5C8002176DE /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
13541356
3E2400381D4FFC31008BDE70 /* OneSignalFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OneSignalFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
13551357
3E24003B1D4FFC31008BDE70 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -2255,6 +2257,7 @@
22552257
3CFA8F442E9087DB00201FE5 /* OSRequestRemoveStartToken.swift */,
22562258
3CFA8F472E9087DB00201FE5 /* OSRequestSetUpdateToken.swift */,
22572259
3CFA8F452E9087DB00201FE5 /* OSRequestRemoveUpdateToken.swift */,
2260+
3CFA8F5A2E9091A200201FE5 /* OSRequestLiveActivityReceiveReceipts.swift */,
22582261
);
22592262
path = Requests;
22602263
sourceTree = "<group>";
@@ -4311,6 +4314,7 @@
43114314
3CFA8F572E9087DB00201FE5 /* OSRequestRemoveUpdateToken.swift in Sources */,
43124315
3CFA8F582E9087DB00201FE5 /* OSLiveActivityRequest.swift in Sources */,
43134316
3CFA8F592E9087DB00201FE5 /* OneSignalLiveActivityAttributes.swift in Sources */,
4317+
3CFA8F5B2E9091A200201FE5 /* OSRequestLiveActivityReceiveReceipts.swift in Sources */,
43144318
);
43154319
runOnlyForDeploymentPostprocessing = 0;
43164320
};

iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,5 +364,6 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP
364364
// Live Activies Executor
365365
#define OS_LIVE_ACTIVITIES_EXECUTOR_UPDATE_TOKENS_KEY @"OS_LIVE_ACTIVITIES_EXECUTOR_UPDATE_TOKENS_KEY"
366366
#define OS_LIVE_ACTIVITIES_EXECUTOR_START_TOKENS_KEY @"OS_LIVE_ACTIVITIES_EXECUTOR_START_TOKENS_KEY"
367+
#define OS_LIVE_ACTIVITIES_EXECUTOR_RECEIVE_RECEIPTS_KEY @"OS_LIVE_ACTIVITIES_EXECUTOR_RECEIVE_RECEIPTS_KEY"
367368

368369
#endif /* OneSignalCommonDefines_h */

iOS_SDK/OneSignalSDK/OneSignalLiveActivities/Source/Executors/OSLiveActivitiesExecutor.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,20 @@ class StartRequestCache: RequestCache {
114114
}
115115
}
116116

117+
class ReceiveReceiptsRequestCache: RequestCache {
118+
// Keep receive receipts requests for up to 30 days.
119+
static let OneMonthInSeconds = TimeInterval(60 * 60 * 24 * 30)
120+
121+
init() {
122+
super.init(cacheKey: OS_LIVE_ACTIVITIES_EXECUTOR_RECEIVE_RECEIPTS_KEY, ttl: ReceiveReceiptsRequestCache.OneMonthInSeconds)
123+
}
124+
}
125+
117126
class OSLiveActivitiesExecutor: OSPushSubscriptionObserver {
118127
// The currently tracked update and start tokens (key) and their associated request (value). THESE ARE NOT THREAD SAFE
119128
let updateTokens: UpdateRequestCache = UpdateRequestCache()
120129
let startTokens: StartRequestCache = StartRequestCache()
130+
let receiveReceipts: ReceiveReceiptsRequestCache = ReceiveReceiptsRequestCache()
121131

122132
// The live activities request dispatch queue, serial. This synchronizes access to `updateTokens` and `startTokens`.
123133
private var requestDispatch: OSDispatchQueue
@@ -182,14 +192,17 @@ class OSLiveActivitiesExecutor: OSPushSubscriptionObserver {
182192
private func caches(_ block: (RequestCache) -> Void) {
183193
block(self.startTokens)
184194
block(self.updateTokens)
195+
block(self.receiveReceipts)
185196
}
186197

187198
private func getCache(_ request: OSLiveActivityRequest) -> RequestCache {
188199
if request is OSLiveActivityUpdateTokenRequest {
189200
return self.updateTokens
201+
} else if request is OSLiveActivityStartTokenRequest {
202+
return self.startTokens
190203
}
191204

192-
return self.startTokens
205+
return self.receiveReceipts
193206
}
194207

195208
private func executeRequest(_ cache: RequestCache, request: OSLiveActivityRequest) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
Modified MIT License
3+
4+
Copyright 2025 OneSignal
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
1. The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
2. All copies of substantial portions of the Software may only be used in connection
17+
with services provided by OneSignal.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
*/
27+
28+
import OneSignalCore
29+
import OneSignalUser
30+
31+
class OSRequestLiveActivityReceiveReceipts: OneSignalRequest, OSLiveActivityRequest {
32+
override var description: String { return "(OSRequestLiveActivityReceiveReceipts) key:\(key) requestSuccessful:\(requestSuccessful) activityType:\(activityType) activityId:\(activityId)" }
33+
34+
var key: String // notification Id
35+
var activityType: String
36+
var activityId: String
37+
var requestSuccessful: Bool
38+
var shouldForgetWhenSuccessful: Bool = true
39+
40+
func prepareForExecution() -> Bool {
41+
guard let appId = OneSignalConfigManager.getAppId() else {
42+
OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the OSRequestLiveActivityReceiveReceipts due to null app ID.")
43+
return false
44+
}
45+
46+
guard let subscriptionId = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId else {
47+
OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the OSRequestLiveActivityReceiveReceipts due to null subscription ID.")
48+
return false
49+
}
50+
51+
self.path = "notifications/\(key)/report_received"
52+
self.parameters = [
53+
"app_id": appId,
54+
"player_id": subscriptionId,
55+
"device_type": 0,
56+
"live_activity_id": activityId,
57+
"live_activity_type": activityType
58+
]
59+
self.method = PUT
60+
61+
return true
62+
}
63+
64+
func supersedes(_ existing: any OSLiveActivityRequest) -> Bool {
65+
return false
66+
}
67+
68+
init(key: String, activityType: String, activityId: String) {
69+
self.key = key
70+
self.activityType = activityType
71+
self.activityId = activityId
72+
self.requestSuccessful = false
73+
super.init()
74+
}
75+
76+
func encode(with coder: NSCoder) {
77+
coder.encode(key, forKey: "key")
78+
coder.encode(activityType, forKey: "activityType")
79+
coder.encode(activityId, forKey: "activityId")
80+
coder.encode(requestSuccessful, forKey: "requestSuccessful")
81+
coder.encode(timestamp, forKey: "timestamp")
82+
}
83+
84+
required init?(coder: NSCoder) {
85+
guard
86+
let key = coder.decodeObject(forKey: "key") as? String,
87+
let activityType = coder.decodeObject(forKey: "activityType") as? String,
88+
let activityId = coder.decodeObject(forKey: "activityId") as? String,
89+
let timestamp = coder.decodeObject(forKey: "timestamp") as? Date
90+
else {
91+
return nil
92+
}
93+
self.key = key
94+
self.activityType = activityType
95+
self.activityId = activityId
96+
self.requestSuccessful = coder.decodeBool(forKey: "requestSuccessful")
97+
super.init()
98+
self.timestamp = timestamp
99+
}
100+
}

0 commit comments

Comments
 (0)