@@ -35,6 +35,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
3535 var addRequestQueue : [ OSRequestCreateSubscription ] = [ ]
3636 var removeRequestQueue : [ OSRequestDeleteSubscription ] = [ ]
3737 var updateRequestQueue : [ OSRequestUpdateSubscription ] = [ ]
38+ var subscriptionModels : [ String : OSSubscriptionModel ] = [ : ]
3839
3940 init ( ) {
4041 // Read unfinished deltas from cache, if any...
@@ -56,20 +57,36 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
5657
5758 // Read unfinished requests from cache, if any...
5859
59- if var addRequestQueue = OneSignalUserDefaults . initShared ( ) . getSavedCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: [ ] ) as? [ OSRequestCreateSubscription ] {
60+ var requestQueue : [ OSRequestCreateSubscription ] = [ ]
61+
62+ if var cachedAddRequestQueue = OneSignalUserDefaults . initShared ( ) . getSavedCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: [ ] ) as? [ OSRequestCreateSubscription ] {
6063 // Hook each uncached Request to the model in the store
61- for (index , request) in addRequestQueue . enumerated ( ) . reversed ( ) {
62- if let subscriptionModel = getSubscriptionModelFromStores ( modelId : request . subscriptionModel . modelId ) ,
63- let identityModel = OneSignalUserManagerImpl . sharedInstance . identityModelStore . getModel ( modelId: request. identityModel . modelId) {
64- // The models exist in the stores , set it to be the Request's models
64+ for request in cachedAddRequestQueue {
65+ // 1. Hook up the subscription model
66+ if let subscriptionModel = getSubscriptionModelFromStores ( modelId: request. subscriptionModel . modelId) {
67+ // a. The model exist in the store , set it to be the Request's models
6568 request. subscriptionModel = subscriptionModel
69+ } else if let subscriptionModel = subscriptionModels [ request. subscriptionModel. modelId] {
70+ // b. The model exists in the dictionary of seen models
71+ request. subscriptionModel = subscriptionModel
72+ } else {
73+ // c. The model has not been seen yet, add to dict
74+ subscriptionModels [ request. subscriptionModel. modelId] = request. subscriptionModel
75+ }
76+ // 2. Hook up the identity model
77+ if let identityModel = OneSignalUserManagerImpl . sharedInstance. identityModelStore. getModel ( modelId: request. identityModel. modelId) {
78+ // a. The model exist in the store
79+ request. identityModel = identityModel
80+ } else if let identityModel = OSUserExecutor . identityModels [ request. identityModel. modelId] {
81+ // b. The model exist in the user executor
6682 request. identityModel = identityModel
6783 } else if !request. prepareForExecution ( ) {
68- // The models do not exist AND this request cannot be sent, drop this Request
69- addRequestQueue . remove ( at : index )
84+ // The model do not exist AND this request cannot be sent, drop this Request
85+ continue
7086 }
87+ requestQueue. append ( request)
7188 }
72- self . addRequestQueue = addRequestQueue
89+ self . addRequestQueue = requestQueue
7390 } else {
7491 OneSignalLog . onesignalLog ( . LL_ERROR, message: " OSSubscriptionOperationExecutor error encountered reading from cache for \( OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY) " )
7592 }
@@ -78,10 +95,13 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
7895 // Hook each uncached Request to the model in the store
7996 for (index, request) in removeRequestQueue. enumerated ( ) . reversed ( ) {
8097 if let subscriptionModel = getSubscriptionModelFromStores ( modelId: request. subscriptionModel. modelId) {
81- // The model exists in the store, set it to be the Request's model
98+ // 1. The model exists in the store, set it to be the Request's model
99+ request. subscriptionModel = subscriptionModel
100+ } else if let subscriptionModel = subscriptionModels [ request. subscriptionModel. modelId] {
101+ // 2. The model exists in the dict of seen subscription models
82102 request. subscriptionModel = subscriptionModel
83103 } else if !request. prepareForExecution ( ) {
84- // The model does not exist AND this request cannot be sent, drop this Request
104+ // 3. The model does not exist AND this request cannot be sent, drop this Request
85105 removeRequestQueue. remove ( at: index)
86106 }
87107 }
@@ -94,10 +114,13 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
94114 // Hook each uncached Request to the model in the store
95115 for (index, request) in updateRequestQueue. enumerated ( ) . reversed ( ) {
96116 if let subscriptionModel = getSubscriptionModelFromStores ( modelId: request. subscriptionModel. modelId) {
97- // The model exists in the store, set it to be the Request's model
117+ // 1. The model exists in the store, set it to be the Request's model
118+ request. subscriptionModel = subscriptionModel
119+ } else if let subscriptionModel = subscriptionModels [ request. subscriptionModel. modelId] {
120+ // 2. The model exists in the dict of seen subscription models
98121 request. subscriptionModel = subscriptionModel
99122 } else if !request. prepareForExecution ( ) {
100- // The models do not exist AND this request cannot be sent, drop this Request
123+ // 3. The models do not exist AND this request cannot be sent, drop this Request
101124 updateRequestQueue. remove ( at: index)
102125 }
103126 }
@@ -202,13 +225,17 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
202225 }
203226
204227 func executeCreateSubscriptionRequest( _ request: OSRequestCreateSubscription ) {
228+ guard !request. sentToClient else {
229+ return
230+ }
205231 guard request. prepareForExecution ( ) else {
206232 return
207233 }
234+ request. sentToClient = true
235+
208236 OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " OSSubscriptionOperationExecutor: executeCreateSubscriptionRequest making request: \( request) " )
209237 OneSignalClient . shared ( ) . execute ( request) { result in
210238 // On success, remove request from cache (even if not hydrating model), and hydrate model
211- // For example, if app restarts and we read in operations between sending this off and getting the response
212239 self . addRequestQueue. removeAll ( where: { $0 == request} )
213240 OneSignalUserDefaults . initShared ( ) . saveCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self . addRequestQueue)
214241
@@ -217,17 +244,26 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
217244 return
218245 }
219246 request. subscriptionModel. hydrate ( response)
220-
221247 } onFailure: { error in
222- self . addRequestQueue. removeAll ( where: { $0 == request} )
223- OneSignalLog . onesignalLog ( . LL_ERROR, message: error. debugDescription)
248+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " OSSubscriptionOperationExecutor create subscription request failed with error: \( error. debugDescription) " )
249+ // TODO: Differentiate error cases
250+ // If the error is not retryable, remove from cache and queue
251+ if let nsError = error as? NSError ,
252+ nsError. code < 500 && nsError. code != 0 {
253+ self . addRequestQueue. removeAll ( where: { $0 == request} )
254+ OneSignalUserDefaults . initShared ( ) . saveCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self . addRequestQueue)
255+ }
224256 }
225257 }
226258
227259 func executeDeleteSubscriptionRequest( _ request: OSRequestDeleteSubscription ) {
260+ guard !request. sentToClient else {
261+ return
262+ }
228263 guard request. prepareForExecution ( ) else {
229264 return
230265 }
266+ request. sentToClient = true
231267
232268 // This request can be executed as-is.
233269 OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " OSSubscriptionOperationExecutor: executeDeleteSubscriptionRequest making request: \( request) " )
@@ -239,17 +275,28 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
239275 OneSignalUserDefaults . initShared ( ) . saveCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self . removeRequestQueue)
240276
241277 } onFailure: { error in
242- self . removeRequestQueue. removeAll ( where: { $0 == request} )
243- OneSignalLog . onesignalLog ( . LL_ERROR, message: error. debugDescription)
278+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " OSSubscriptionOperationExecutor delete subscription request failed with error: \( error. debugDescription) " )
279+ // TODO: Differentiate error cases
280+ // If the error is not retryable, remove from cache and queue
281+ if let nsError = error as? NSError ,
282+ nsError. code < 500 && nsError. code != 0 {
283+ self . removeRequestQueue. removeAll ( where: { $0 == request} )
284+ OneSignalUserDefaults . initShared ( ) . saveCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self . removeRequestQueue)
285+ }
244286 }
245287 }
246288
247289 func executeUpdateSubscriptionRequest( _ request: OSRequestUpdateSubscription ) {
248- OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " OSSubscriptionOperationExecutor: executeUpdateSubscriptionRequest making request: \( request) " )
249-
290+ guard !request. sentToClient else {
291+ return
292+ }
250293 guard request. prepareForExecution ( ) else {
251294 return
252295 }
296+ request. sentToClient = true
297+
298+ OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " OSSubscriptionOperationExecutor: executeUpdateSubscriptionRequest making request: \( request) " )
299+
253300 OneSignalClient . shared ( ) . execute ( request) { _ in
254301
255302 // On success, remove request from cache. No model hydration occurs.
@@ -258,8 +305,14 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor {
258305 OneSignalUserDefaults . initShared ( ) . saveCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self . updateRequestQueue)
259306
260307 } onFailure: { error in
261- self . updateRequestQueue. removeAll ( where: { $0 == request} )
262- OneSignalLog . onesignalLog ( . LL_ERROR, message: error. debugDescription)
308+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " OSSubscriptionOperationExecutor update subscription request failed with error: \( error. debugDescription) " )
309+ // TODO: Differentiate error cases
310+ // If the error is not retryable, remove from cache and queue
311+ if let nsError = error as? NSError ,
312+ nsError. code < 500 && nsError. code != 0 {
313+ self . updateRequestQueue. removeAll ( where: { $0 == request} )
314+ OneSignalUserDefaults . initShared ( ) . saveCodeableData ( forKey: OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self . updateRequestQueue)
315+ }
263316 }
264317 }
265318}
0 commit comments