Skip to content

Commit 059227c

Browse files
committed
reading Deltas and Requests from cache, connect to models
* Motivation: When initializing the delta queue and request queue, the executors need to hook up the Deltas' and Requests' models to the ones in the store, so that they can get the updated information needed to send the requests, and hydrate the correct models. * When model stores, deltas, and requests are initialized from cache, they lose their references as they are new instances. * To simplify uncaching, we maintain separate request queues for each request type in the executor * Also needed to update some `let` properties to be `var`
1 parent d84070d commit 059227c

File tree

8 files changed

+253
-100
lines changed

8 files changed

+253
-100
lines changed

iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP
285285
#define OS_EXTERNAL_ID @"external_id"
286286

287287
#define OS_RETAIN_PREVIOUS_USER @"retain_previous_user"
288+
#define OS_ON_USER_WILL_CHANGE @"OS_ON_USER_WILL_CHANGE"
288289

290+
// Models and Model Stores
289291
#define OS_IDENTITY_MODEL_KEY @"OS_IDENTITY_MODEL_KEY"
290292
#define OS_IDENTITY_MODEL_STORE_KEY @"OS_IDENTITY_MODEL_STORE_KEY"
291293
#define OS_PROPERTIES_MODEL_KEY @"OS_PROPERTIES_MODEL_KEY"
@@ -294,6 +296,7 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP
294296
#define OS_PUSH_SUBSCRIPTION_MODEL_STORE_KEY @"OS_PUSH_SUBSCRIPTION_MODEL_STORE_KEY"
295297
#define OS_SUBSCRIPTION_MODEL_STORE_KEY @"OS_SUBSCRIPTION_MODEL_STORE_KEY"
296298

299+
// Deltas
297300
#define OS_ADD_ALIAS_DELTA @"OS_ADD_ALIAS_DELTA"
298301
#define OS_REMOVE_ALIAS_DELTA @"OS_REMOVE_ALIAS_DELTA"
299302

@@ -303,16 +306,25 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP
303306
#define OS_REMOVE_SUBSCRIPTION_DELTA @"OS_REMOVE_SUBSCRIPTION_DELTA"
304307
#define OS_UPDATE_SUBSCRIPTION_DELTA @"OS_UPDATE_SUBSCRIPTION_DELTA"
305308

309+
// Operation Repo
306310
#define OS_OPERATION_REPO_DELTA_QUEUE_KEY @"OS_OPERATION_REPO_DELTA_QUEUE_KEY"
307311

312+
// User Executor
308313
#define OS_USER_EXECUTOR_REQUEST_QUEUE_KEY @"OS_USER_EXECUTOR_REQUEST_QUEUE_KEY"
314+
315+
// Identity Executor
309316
#define OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY @"OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY"
310-
#define OS_IDENTITY_EXECUTOR_REQUEST_QUEUE_KEY @"OS_IDENTITY_EXECUTOR_REQUEST_QUEUE_KEY"
317+
#define OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY @"OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY"
318+
#define OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY @"OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY"
319+
320+
// Property Executor
311321
#define OS_PROPERTIES_EXECUTOR_DELTA_QUEUE_KEY @"OS_PROPERTIES_EXECUTOR_DELTA_QUEUE_KEY"
312-
#define OS_PROPERTIES_EXECUTOR_REQUEST_QUEUE_KEY @"OS_PROPERTIES_EXECUTOR_REQUEST_QUEUE_KEY"
313-
#define OS_SUBSCRIPTION_EXECUTOR_DELTA_QUEUE_KEY @"OS_SUBSCRIPTION_EXECUTOR_DELTA_QUEUE_KEY"
314-
#define OS_SUBSCRIPTION_EXECUTOR_REQUEST_QUEUE_KEY @"OS_SUBSCRIPTION_EXECUTOR_REQUEST_QUEUE_KEY"
322+
#define OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY @"OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY"
315323

316-
#define OS_ON_USER_WILL_CHANGE @"OS_ON_USER_WILL_CHANGE"
324+
// Subscription Executor
325+
#define OS_SUBSCRIPTION_EXECUTOR_DELTA_QUEUE_KEY @"OS_SUBSCRIPTION_EXECUTOR_DELTA_QUEUE_KEY"
326+
#define OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY @"OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY"
327+
#define OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY @"OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY"
328+
#define OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY @"OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY"
317329

318330
#endif /* OneSignalCommonDefines_h */

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSDelta.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ open class OSDelta: NSObject, NSCoding {
3434
public let name: String
3535
public let deltaId: String
3636
public let timestamp: Date
37-
public let model: OSModel
37+
public var model: OSModel
3838
public let property: String
3939
public let value: Any
4040

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,24 @@ open class OSModelStore<TModel: OSModel>: NSObject {
6060
}
6161

6262
/**
63-
Uses the ID that is used a key to store models in the store's models dictionary.
63+
Uses the ID that is used as the key to store models in the store's models dictionary.
6464
Examples: "[email protected]" for a subscription model or `OS_IDENTITY_MODEL_KEY` for an identity model.
6565
*/
6666
public func getModel(key: String) -> TModel? {
6767
return self.models[key]
6868
}
69+
70+
/**
71+
Uses the `modelId` to get the corresponding model in the store's models dictionary.
72+
*/
73+
public func getModel(modelId: String) -> TModel? {
74+
for model in models.values {
75+
if model.modelId == modelId {
76+
return model
77+
}
78+
}
79+
return nil
80+
}
6981

7082
public func getModels() -> [String: TModel] {
7183
return self.models

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationExecutor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727

2828
import OneSignalCore
2929

30+
// TODO: Concrete executors drop OSDeltas and Requests when initializing from the cache, when they cannot be connected to their respective models anymore. Revisit this behavior of dropping.
31+
3032
public protocol OSOperationExecutor {
3133
var supportedDeltas: [String] { get }
3234
var deltaQueue: [OSDelta] { get }
33-
var requestQueue: [OneSignalRequest] { get }
3435

3536
func enqueueDelta(_ delta: OSDelta)
3637
func cacheDeltaQueue()
3738
func processDeltaQueue()
3839

39-
func enqueueRequest(_ request: OneSignalRequest)
4040
func processRequestQueue()
4141
}

iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,60 @@ import OneSignalCore
3131
class OSIdentityOperationExecutor: OSOperationExecutor {
3232
var supportedDeltas: [String] = [OS_ADD_ALIAS_DELTA, OS_REMOVE_ALIAS_DELTA]
3333
var deltaQueue: [OSDelta] = []
34-
var requestQueue: [OneSignalRequest] = []
34+
// To simplify uncaching, we maintain separate request queues for each type
35+
var addRequestQueue: [OSRequestAddAliases] = []
36+
var removeRequestQueue: [OSRequestRemoveAlias] = []
3537

3638
init() {
37-
// Read unfinished deltas and requests from cache, if any...
38-
39-
if let deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] {
39+
// Read unfinished deltas from cache, if any...
40+
if var deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] {
41+
// Hook each uncached Delta to the model in the store
42+
for (index, delta) in deltaQueue.enumerated().reversed() {
43+
if let modelInStore = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: delta.model.modelId) {
44+
// The model exists in the store, set it to be the Delta's model
45+
delta.model = modelInStore
46+
} else {
47+
// The model does not exist, drop this Delta
48+
deltaQueue.remove(at: index)
49+
}
50+
}
4051
self.deltaQueue = deltaQueue
4152
} else {
42-
// log error
53+
OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor error encountered reading from cache for \(OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY)")
4354
}
44-
45-
if let requestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_REQUEST_QUEUE_KEY, defaultValue: []) as? [OneSignalRequest] {
46-
self.requestQueue = requestQueue
55+
56+
// Read unfinished requests from cache, if any...
57+
58+
if var addRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestAddAliases] {
59+
// Hook each uncached Request to the model in the store
60+
for (index, request) in addRequestQueue.enumerated().reversed() {
61+
if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) {
62+
// The model exists in the store, set it to be the Request's models
63+
request.identityModel = identityModel
64+
} else if !request.prepareForExecution() {
65+
// The models do not exist AND this request cannot be sent, drop this Request
66+
addRequestQueue.remove(at: index)
67+
}
68+
}
69+
self.addRequestQueue = addRequestQueue
70+
} else {
71+
OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor error encountered reading from cache for \(OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY)")
72+
}
73+
74+
if var removeRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestRemoveAlias] {
75+
// Hook each uncached Request to the model in the store
76+
for (index, request) in removeRequestQueue.enumerated().reversed() {
77+
if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) {
78+
// The model exists in the store, set it to be the Request's model
79+
request.identityModel = identityModel
80+
} else if !request.prepareForExecution() {
81+
// The model does not exist AND this request cannot be sent, drop this Request
82+
removeRequestQueue.remove(at: index)
83+
}
84+
}
85+
self.removeRequestQueue = removeRequestQueue
4786
} else {
48-
// log error
87+
OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor error encountered reading from cache for \(OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY)")
4988
}
5089
}
5190

@@ -73,48 +112,47 @@ class OSIdentityOperationExecutor: OSOperationExecutor {
73112
switch delta.name {
74113
case OS_ADD_ALIAS_DELTA:
75114
let request = OSRequestAddAliases(aliases: aliases, identityModel: model)
76-
enqueueRequest(request)
115+
addRequestQueue.append(request)
77116

78117
case OS_REMOVE_ALIAS_DELTA:
79118
if let label = aliases.first?.key {
80119
let request = OSRequestRemoveAlias(labelToRemove: label, identityModel: model)
81-
enqueueRequest(request)
120+
removeRequestQueue.append(request)
82121
}
83-
// Log error
84122

85123
default:
86-
// Log error
87124
OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSIdentityOperationExecutor met incompatible OSDelta type: \(delta)")
88125
}
89126
}
90127

91128
self.deltaQueue = [] // TODO: Check that we can simply clear all the deltas in the deltaQueue
92129

93130
// persist executor's requests (including new request) to storage
94-
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REQUEST_QUEUE_KEY, withValue: self.requestQueue)
131+
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue)
132+
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue)
95133

96134
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY, withValue: self.deltaQueue) // This should be empty, can remove instead?
97135

98136
processRequestQueue()
99137
}
100138

101-
func enqueueRequest(_ request: OneSignalRequest) {
102-
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSIdentityOperationExecutor enqueueRequest: \(request)")
103-
requestQueue.append(request)
104-
}
105-
106139
func processRequestQueue() {
140+
let requestQueue: [OneSignalRequest] = addRequestQueue + removeRequestQueue
141+
107142
if requestQueue.isEmpty {
108143
return
109144
}
110145

111-
for request in requestQueue {
146+
// Sort the requestQueue by timestamp
147+
for request in requestQueue.sorted(by: { first, second in
148+
return first.timestamp < second.timestamp
149+
}) {
112150
if request.isKind(of: OSRequestAddAliases.self), let addAliasesRequest = request as? OSRequestAddAliases {
113151
executeAddAliasesRequest(addAliasesRequest)
114152
} else if request.isKind(of: OSRequestRemoveAlias.self), let removeAliasRequest = request as? OSRequestRemoveAlias {
115153
executeRemoveAliasRequest(removeAliasRequest)
116154
} else {
117-
// Log Error
155+
OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSIdentityOperationExecutor.processRequestQueue met incompatible OneSignalRequest type: \(request).")
118156
}
119157
}
120158
}
@@ -128,15 +166,15 @@ class OSIdentityOperationExecutor: OSOperationExecutor {
128166

129167
// On success, remove request from cache, and hydrate model
130168
// For example, if app restarts and we read in operations between sending this off and getting the response
131-
self.requestQueue.removeAll(where: { $0 == request})
132-
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REQUEST_QUEUE_KEY, withValue: self.requestQueue)
169+
self.addRequestQueue.removeAll(where: { $0 == request})
170+
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue)
133171

134172
// instead: modelstore.hydratewithresponse with modelid passed in.. request.modeltoupdate.modelId
135173
// store can determine if modelid is same, then hydrate or do nothign
136174
request.identityModel.hydrate(response)
137175

138176
} onFailure: { error in
139-
self.requestQueue.removeAll(where: { $0 == request})
177+
self.addRequestQueue.removeAll(where: { $0 == request})
140178
OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription)
141179
}
142180
}
@@ -151,13 +189,13 @@ class OSIdentityOperationExecutor: OSOperationExecutor {
151189

152190
// On success, remove request from cache, and hydrate model
153191
// For example, if app restarts and we read in operations between sending this off and getting the response
154-
self.requestQueue.removeAll(where: { $0 == request})
155-
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REQUEST_QUEUE_KEY, withValue: self.requestQueue)
192+
self.removeRequestQueue.removeAll(where: { $0 == request})
193+
OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue)
156194

157195
request.identityModel.hydrate(response)
158196

159197
} onFailure: { error in
160-
self.requestQueue.removeAll(where: { $0 == request})
198+
self.removeRequestQueue.removeAll(where: { $0 == request})
161199
OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription)
162200
}
163201
}

0 commit comments

Comments
 (0)