diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h index f5c107876..30658efd4 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h @@ -340,6 +340,7 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP #define OS_UPDATE_SUBSCRIPTION_DELTA @"OS_UPDATE_SUBSCRIPTION_DELTA" // Operation Repo +#define OS_OPERATION_REPO @"OS_OPERATION_REPO" #define OS_OPERATION_REPO_DELTA_QUEUE_KEY @"OS_OPERATION_REPO_DELTA_QUEUE_KEY" // User Executor diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift index cb5bb2ed2..5602d1dc1 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift @@ -31,9 +31,11 @@ import OneSignalCore public protocol OSModelStoreListener: OSModelStoreChangedHandler { associatedtype TModel: OSModel + var operationRepo: OSOperationRepo { get } + var store: OSModelStore { get } - init(store: OSModelStore) + init(store: OSModelStore, operationRepo: OSOperationRepo) func getAddModelDelta(_ model: TModel) -> OSDelta? @@ -59,13 +61,13 @@ extension OSModelStoreListener { return } if let delta = getAddModelDelta(addedModel) { - OSOperationRepo.sharedInstance.enqueueDelta(delta) + operationRepo.enqueueDelta(delta) } } public func onUpdated(_ args: OSModelChangedArgs) { if let delta = getUpdateModelDelta(args) { - OSOperationRepo.sharedInstance.enqueueDelta(delta) + operationRepo.enqueueDelta(delta) } } @@ -76,7 +78,7 @@ extension OSModelStoreListener { return } if let delta = getRemoveModelDelta(removedModel) { - OSOperationRepo.sharedInstance.enqueueDelta(delta) + operationRepo.enqueueDelta(delta) } } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift index 1b6b40d1c..283193b53 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift @@ -32,8 +32,7 @@ import OneSignalCore The OSOperationRepo is a static singleton. OSDeltas are enqueued when model store observers observe changes to their models, and sorted to their appropriate executors. */ -public class OSOperationRepo: NSObject { - public static let sharedInstance = OSOperationRepo() +public class OSOperationRepo { private var hasCalledStart = false // The Operation Repo dispatch queue, serial. This synchronizes access to `deltaQueue` and flushing behavior. @@ -47,16 +46,37 @@ public class OSOperationRepo: NSObject { // TODO: This could come from a config, plist, method, remote params var pollIntervalMilliseconds = Int(POLL_INTERVAL_MS) public var paused = false + let jwtConfig: OSUserJwtConfig /** - Initilize this Operation Repo. Read from the cache. Executors may not be available by this time. - If everything starts up on initialize(), order can matter, ideally not but it can. - Likely call init on this from oneSignal but exeuctors can come from diff modules. + Sets the jwt config and uncaches + */ + public init(jwtConfig: OSUserJwtConfig) { + self.jwtConfig = jwtConfig + self.jwtConfig.subscribe(self, key: OS_OPERATION_REPO) + print("❌ OSOperationRepo init(\(String(describing: jwtConfig.isRequired))) called") + + // Read the Deltas from cache, if any... + guard let deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] else { + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSOperationRepo is unable to uncache the OSDelta queue.") + return + } + self.deltaQueue = deltaQueue + } + + /** + Start this Operation Repo. */ public func start() { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } + + guard jwtConfig.isRequired != nil else { + print("❌ OSOperationRepo.start() returning early due to unknown Identity Verification status.") + return + } + guard !hasCalledStart else { return } @@ -68,13 +88,6 @@ public class OSOperationRepo: NSObject { selector: #selector(self.addFlushDeltaQueueToDispatchQueue), name: Notification.Name(OS_ON_USER_WILL_CHANGE), object: nil) - // Read the Deltas from cache, if any... - if let deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] { - self.deltaQueue = deltaQueue - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo.start() with deltaQueue: \(deltaQueue)") - } else { - OneSignalLog.onesignalLog(.LL_ERROR, message: "OSOperationRepo.start() is unable to uncache the OSDelta queue.") - } pollFlushQueue() } @@ -87,13 +100,12 @@ public class OSOperationRepo: NSObject { } /** - Add and start an executor. + Add an executor. */ public func addExecutor(_ executor: OSOperationExecutor) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } - start() executors.append(executor) for delta in executor.supportedDeltas { deltasToExecutorMap[delta] = executor @@ -111,7 +123,6 @@ public class OSOperationRepo: NSObject { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } - start() self.dispatchQueue.async { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo enqueueDelta: \(delta)") self.deltaQueue.append(delta) @@ -140,8 +151,6 @@ public class OSOperationRepo: NSObject { OSBackgroundTaskManager.beginBackgroundTask(OPERATION_REPO_BACKGROUND_TASK) } - self.start() - if !self.deltaQueue.isEmpty { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo flushDeltaQueue in background: \(inBackground) with queue: \(self.deltaQueue)") } @@ -174,6 +183,30 @@ public class OSOperationRepo: NSObject { } } +extension OSOperationRepo: OSUserJwtConfigListener { + public func onRequiresUserAuthChanged(from: Bool?, to: Bool?) { + print("❌ OSOperationRepo onRequiresUserAuthChanged from \(String(describing: from)) to \(String(describing: to))") + // If auth changed from false or unknown to true, process deltas + if to == true { + removeInvalidDeltas() + } + start() + } + + public func onJwtUpdated(externalId: String, to: String?) { + print("❌ OSOperationRepo onJwtUpdated for \(externalId) to \(String(describing: to))") + } + + /** + TODO: The operation repo cannot easily remove invalid Deltas that do not have an External ID. + Deltas have an Identity Model ID only and would need to access the User module to find the corresponding Identity Model. + Executors will handle this. + */ + func removeInvalidDeltas() { + print("❌ OSOperationRepo removeInvalidDeltas TODO!") + } +} + extension OSOperationRepo: OSLoggable { public func logSelf() { print("💛 Operation Repo: executors") diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift index 822ca2120..1ad66a093 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift @@ -335,7 +335,7 @@ extension OSUserExecutor { self.executePendingRequests() } } - OSOperationRepo.sharedInstance.paused = false + OneSignalUserManagerImpl.sharedInstance.operationRepo.paused = false } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor create user request failed with error: \(error.debugDescription)") if let nsError = error as? NSError { @@ -344,7 +344,7 @@ extension OSUserExecutor { // A failed create user request would leave the SDK in a bad state // Don't remove the request from cache and pause the operation repo // We will retry this request on a new session - OSOperationRepo.sharedInstance.paused = true + OneSignalUserManagerImpl.sharedInstance.operationRepo.paused = true request.sentToClient = false } } else { diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift index 517946abe..d41b839fd 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift @@ -30,9 +30,11 @@ import OneSignalCore import OneSignalOSCore class OSIdentityModelStoreListener: OSModelStoreListener { + let operationRepo: OSOperationRepo var store: OSModelStore - required init(store: OSModelStore) { + required init(store: OSModelStore, operationRepo: OSOperationRepo) { + self.operationRepo = operationRepo self.store = store } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift index 611228801..cec233e43 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift @@ -30,9 +30,11 @@ import OneSignalCore import OneSignalOSCore class OSPropertiesModelStoreListener: OSModelStoreListener { + let operationRepo: OSOperationRepo var store: OSModelStore - required init(store: OSModelStore) { + required init(store: OSModelStore, operationRepo: OSOperationRepo) { + self.operationRepo = operationRepo self.store = store } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift index 6ffe8c9ac..a7965a130 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift @@ -30,9 +30,11 @@ import OneSignalCore import OneSignalOSCore class OSSubscriptionModelStoreListener: OSModelStoreListener { + let operationRepo: OSOperationRepo var store: OSModelStore - required init(store: OSModelStore) { + required init(store: OSModelStore, operationRepo: OSOperationRepo) { + self.operationRepo = operationRepo self.store = store } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index e083f934f..f5606b742 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -173,6 +173,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { let pushSubscriptionModelStore = OSModelStore(changeSubscription: OSEventProducer(), storeKey: OS_PUSH_SUBSCRIPTION_MODEL_STORE_KEY) // These must be initialized in init() + let operationRepo: OSOperationRepo let identityModelStoreListener: OSIdentityModelStoreListener let propertiesModelStoreListener: OSPropertiesModelStoreListener let subscriptionModelStoreListener: OSSubscriptionModelStoreListener @@ -186,10 +187,11 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { private init(jwtConfig: OSUserJwtConfig) { self.jwtConfig = jwtConfig - self.identityModelStoreListener = OSIdentityModelStoreListener(store: identityModelStore) - self.propertiesModelStoreListener = OSPropertiesModelStoreListener(store: propertiesModelStore) - self.subscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: subscriptionModelStore) - self.pushSubscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: pushSubscriptionModelStore) + self.operationRepo = OSOperationRepo(jwtConfig: jwtConfig) + self.identityModelStoreListener = OSIdentityModelStoreListener(store: identityModelStore, operationRepo: operationRepo) + self.propertiesModelStoreListener = OSPropertiesModelStoreListener(store: propertiesModelStore, operationRepo: operationRepo) + self.subscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: subscriptionModelStore, operationRepo: operationRepo) + self.pushSubscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: pushSubscriptionModelStore, operationRepo: operationRepo) self.pushSubscriptionImpl = OSPushSubscriptionImpl(pushSubscriptionModelStore: pushSubscriptionModelStore) } @@ -226,7 +228,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { // Setup the executors // The OSUserExecutor has to run first, before other executors self.userExecutor = OSUserExecutor(newRecordsState: newRecordsState, jwtConfig: jwtConfig) - OSOperationRepo.sharedInstance.start() + operationRepo.start() // Cannot initialize these executors in `init` as they reference the sharedInstance let propertyExecutor = OSPropertyOperationExecutor(newRecordsState: newRecordsState, jwtConfig: jwtConfig) @@ -235,9 +237,9 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { self.propertyExecutor = propertyExecutor self.identityExecutor = identityExecutor self.subscriptionExecutor = subscriptionExecutor - OSOperationRepo.sharedInstance.addExecutor(identityExecutor) - OSOperationRepo.sharedInstance.addExecutor(propertyExecutor) - OSOperationRepo.sharedInstance.addExecutor(subscriptionExecutor) + operationRepo.addExecutor(identityExecutor) + operationRepo.addExecutor(propertyExecutor) + operationRepo.addExecutor(subscriptionExecutor) // Path 2. There is a legacy player to migrate if let legacyPlayerId = OneSignalUserDefaults.initShared().getSavedString(forKey: OSUD_LEGACY_PLAYER_ID, defaultValue: nil) { @@ -561,7 +563,7 @@ extension OneSignalUserManagerImpl { start() userExecutor!.executePendingRequests() - OSOperationRepo.sharedInstance.paused = false + operationRepo.paused = false updatePropertiesDeltas(property: .session_count, value: 1) // Fetch the user's data if there is a onesignal_id @@ -594,7 +596,7 @@ extension OneSignalUserManagerImpl { property: property.rawValue, value: value ) - OSOperationRepo.sharedInstance.enqueueDelta(delta) + operationRepo.enqueueDelta(delta) } /// Time processors forward the session time to this method. @@ -612,7 +614,7 @@ extension OneSignalUserManagerImpl { */ @objc public func runBackgroundTasks() { - OSOperationRepo.sharedInstance.addFlushDeltaQueueToDispatchQueue(inBackground: true) + operationRepo.addFlushDeltaQueueToDispatchQueue(inBackground: true) } }