diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift index c5eb2a5c4..7577310d3 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift @@ -31,11 +31,10 @@ import OneSignalOSCore /** Involved in the login process and responsible for Identify User and Create User. - Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestTransferSubscription`, `OSRequestFetchUser`, `OSRequestFetchIdentityBySubscription`. + Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestFetchUser`, `OSRequestFetchIdentityBySubscription`. */ class OSUserExecutor { static var userRequestQueue: [OSUserRequest] = [] - static var transferSubscriptionRequestQueue: [OSRequestTransferSubscription] = [] // The User executor dispatch queue, serial. This synchronizes access to the request queues. private static let dispatchQueue = DispatchQueue(label: "OneSignal.OSUserExecutor", target: .global()) @@ -99,30 +98,28 @@ class OSUserExecutor { } self.userRequestQueue = userRequestQueue OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) - // Read unfinished Transfer Subscription requests from cache, if any... - if let transferSubscriptionRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestTransferSubscription] { - // We only care about the last transfer subscription request - if let request = transferSubscriptionRequestQueue.last { - // Hook the uncached Request to the model in the store - if request.subscriptionModel.modelId == OneSignalUserManagerImpl.sharedInstance.user.pushSubscriptionModel.modelId { - // The model exist, set it to be the Request's model - request.subscriptionModel = OneSignalUserManagerImpl.sharedInstance.user.pushSubscriptionModel - self.transferSubscriptionRequestQueue = [request] - } else if !request.prepareForExecution() { - // The model do not exist AND this request cannot be sent, drop this Request - OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor.start() dropped: \(request)") - self.transferSubscriptionRequestQueue = [] - } - } - } else { - OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor error encountered reading from cache for \(OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY)") - } - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY, withValue: self.transferSubscriptionRequestQueue) + migrateTransferSubscriptionRequests() executePendingRequests() } } + /** + Read Transfer Subscription requests from cache, if any. + As of `5.2.3`, the SDK will no longer send Transfer Subscription requests, so migrate the request into an equivalent Create User request. + */ + static private func migrateTransferSubscriptionRequests() { + if let transferSubscriptionRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY, defaultValue: nil) as? [OSRequestTransferSubscription] { + OneSignalUserDefaults.initShared().removeValue(forKey: OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY) + + // Translate the last request into a Create User request, if the current user is the same + if let request = transferSubscriptionRequestQueue.last, + OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.aliasId) { + createUser(OneSignalUserManagerImpl.sharedInstance.user) + } + } + } + static private func getIdentityModel(_ modelId: String) -> OSIdentityModel? { return OneSignalUserManagerImpl.sharedInstance.getIdentityModel(modelId) } @@ -133,41 +130,27 @@ class OSUserExecutor { static func appendToQueue(_ request: OSUserRequest) { self.dispatchQueue.async { - if request.isKind(of: OSRequestTransferSubscription.self), let req = request as? OSRequestTransferSubscription { - self.transferSubscriptionRequestQueue.append(req) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY, withValue: self.transferSubscriptionRequestQueue) - } else { - self.userRequestQueue.append(request) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) - } + self.userRequestQueue.append(request) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) } } static func removeFromQueue(_ request: OSUserRequest) { self.dispatchQueue.async { - if request.isKind(of: OSRequestTransferSubscription.self), let req = request as? OSRequestTransferSubscription { - transferSubscriptionRequestQueue.removeAll(where: { $0 == req}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY, withValue: self.transferSubscriptionRequestQueue) - } else { - userRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) - } + userRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) } } static func executePendingRequests() { self.dispatchQueue.async { - let requestQueue: [OSUserRequest] = userRequestQueue + transferSubscriptionRequestQueue - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSUserExecutor.executePendingRequests called with queue \(requestQueue)") + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSUserExecutor.executePendingRequests called with queue \(userRequestQueue)") - if requestQueue.isEmpty { + if userRequestQueue.isEmpty { return } - // Sort the requestQueue by timestamp - for request in requestQueue.sorted(by: { first, second in - return first.timestamp < second.timestamp - }) { + for request in userRequestQueue { // Return as soon as we reach an un-executable request if !request.prepareForExecution() { OneSignalLog.onesignalLog(.LL_WARN, message: "OSUserExecutor.executePendingRequests() is blocked by unexecutable request \(request)") @@ -183,9 +166,6 @@ class OSUserExecutor { } else if request.isKind(of: OSRequestIdentifyUser.self), let identifyUserRequest = request as? OSRequestIdentifyUser { executeIdentifyUserRequest(identifyUserRequest) return - } else if request.isKind(of: OSRequestTransferSubscription.self), let transferSubscriptionRequest = request as? OSRequestTransferSubscription { - executeTransferPushSubscriptionRequest(transferSubscriptionRequest) - return } else if request.isKind(of: OSRequestFetchUser.self), let fetchUserRequest = request as? OSRequestFetchUser { executeFetchUserRequest(fetchUserRequest) return @@ -210,18 +190,28 @@ extension OSUserExecutor { executePendingRequests() } + /** + This Create User call expects an external ID and the Identity Model to hydrate with the OneSignal ID + */ + static func createUser(aliasLabel: String, aliasId: String, identityModel: OSIdentityModel) { + let request = OSRequestCreateUser(aliasLabel: aliasLabel, aliasId: aliasId, identityModel: identityModel) + appendToQueue(request) + executePendingRequests() + } + static func executeCreateUserRequest(_ request: OSRequestCreateUser) { guard !request.sentToClient else { return } guard request.prepareForExecution() else { - // Currently there are no requirements needed before sending this request + // Currently there are no requirements needed before sending this request, so this will set the path return } request.sentToClient = true - // Hook up push subscription model, it may be updated with a subscription_id, etc. - if let pushSubscriptionModel = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModel(modelId: request.pushSubscriptionModel.modelId) { + // Hook up push subscription model if exists, it may be updated with a subscription_id, etc. + if let modelId = request.pushSubscriptionModel?.modelId, + let pushSubscriptionModel = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModel(modelId: modelId) { request.pushSubscriptionModel = pushSubscriptionModel request.updatePushSubscriptionModel(pushSubscriptionModel) } @@ -237,11 +227,12 @@ extension OSUserExecutor { // If this user already exists and we logged into an external_id, fetch the user data // TODO: Only do this if response code is 200 or 202 - // Fetch the user only if its the current user + // Fetch the user only if its the current user and non-anonymous if OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModel), let identity = request.parameters?["identity"] as? [String: String], - let externalId = identity[OS_EXTERNAL_ID] { - fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: externalId, identityModel: request.identityModel) + let onesignalId = request.identityModel.onesignalId, + identity[OS_EXTERNAL_ID] != nil { + fetchUser(aliasLabel: OS_ONESIGNAL_ID, aliasId: onesignalId, identityModel: request.identityModel) } else { executePendingRequests() } @@ -339,19 +330,27 @@ extension OSUserExecutor { OneSignalCoreImpl.sharedClient().execute(request) { _ in removeFromQueue(request) + guard let onesignalId = request.identityModelToIdentify.onesignalId else { + OneSignalLog.onesignalLog(.LL_ERROR, message: "executeIdentifyUserRequest succeeded but is now missing OneSignal ID!") + executePendingRequests() + return + } + + // Need to hydrate the identity model for current user or past user with pending requests + let aliases = [ + OS_ONESIGNAL_ID: onesignalId, + request.aliasLabel: request.aliasId + ] + request.identityModelToUpdate.hydrate(aliases) + // the anonymous user has been identified, still need to Fetch User as we cleared local data // Fetch the user only if its the current user if OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModelToUpdate) { - fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) + fetchUser(aliasLabel: OS_ONESIGNAL_ID, aliasId: onesignalId, identityModel: request.identityModelToUpdate) } else { - // Need to hydrate the identity model for any pending requests - if let osid = request.identityModelToIdentify.onesignalId { - request.identityModelToUpdate.hydrate([OS_ONESIGNAL_ID: osid]) - } executePendingRequests() } } onFailure: { error in - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "executeIdentifyUserRequest failed with error \(error.debugDescription)") if let nsError = error as? NSError { let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) if responseType == .conflict { @@ -359,14 +358,13 @@ extension OSUserExecutor { OneSignalLog.onesignalLog(.LL_DEBUG, message: "executeIdentifyUserRequest returned error code user-2. Now handling user-2 error response... switch to this user.") removeFromQueue(request) - // Transfer the push subscription, and fetch only if it's the current user + if OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModelToUpdate) { - fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) - transferPushSubscriptionTo(aliasLabel: request.aliasLabel, aliasId: request.aliasId) + // Generate a Create User request, if it's still the current user + createUser(OneSignalUserManagerImpl.sharedInstance.user) } else { - // Use external_id for any pending requests, avoiding a fetch to hydrate onesignal_id - request.identityModelToUpdate.primaryAliasLabel = .external_id - executePendingRequests() + // This will hydrate the OneSignal ID for any pending requests + createUser(aliasLabel: request.aliasLabel, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) } } else if responseType == .invalid || responseType == .unauthorized { // Failed, no retry @@ -390,45 +388,6 @@ extension OSUserExecutor { } } - static func transferPushSubscriptionTo(aliasLabel: String, aliasId: String) { - // TODO: Where to get pushSubscriptionModel for this request - let request = OSRequestTransferSubscription( - subscriptionModel: OneSignalUserManagerImpl.sharedInstance.user.pushSubscriptionModel, - aliasLabel: aliasLabel, - aliasId: aliasId - ) - - appendToQueue(request) - - executePendingRequests() - } - - static func executeTransferPushSubscriptionRequest(_ request: OSRequestTransferSubscription) { - guard !request.sentToClient else { - return - } - guard request.prepareForExecution() else { - // Missing subscriptionId - OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSUserExecutor.executeTransferPushSubscriptionRequest with request \(request) cannot be executed due to failing prepareForExecution()") - return - } - request.sentToClient = true - OneSignalCoreImpl.sharedClient().execute(request) { _ in - removeFromQueue(request) - executePendingRequests() - } onFailure: { error in - OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor executeTransferPushSubscriptionRequest failed with error: \(error.debugDescription)") - if let nsError = error as? NSError { - let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) - if responseType != .retryable { - // Fail, no retry, remove from cache and queue - removeFromQueue(request) - } - } - executePendingRequests() - } - } - static func fetchUser(aliasLabel: String, aliasId: String, identityModel: OSIdentityModel, onNewSession: Bool = false) { let request = OSRequestFetchUser(identityModel: identityModel, aliasLabel: aliasLabel, aliasId: aliasId, onNewSession: onNewSession) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift index 639e47c50..6e70b5057 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift @@ -29,23 +29,7 @@ import Foundation import OneSignalCore import OneSignalOSCore -// By matching the enum name to the raw value, it will always stringify correctly -enum OSDefaultAlias: String { - // swiftlint:disable identifier_name - case onesignal_id = "onesignal_id" - case external_id = "external_id" - // swiftlint:enable identifier_name -} - class OSIdentityModel: OSModel { - /** - Set either `onesignal_id` or `external_id`, representing the alias that will be used in requests. - */ - var primaryAliasLabel: OSDefaultAlias = .onesignal_id - var primaryAliasId: String? { - return if primaryAliasLabel == .external_id { externalId } else { onesignalId } - } - var onesignalId: String? { return internalGetAlias(OS_ONESIGNAL_ID) } @@ -73,7 +57,6 @@ class OSIdentityModel: OSModel { aliasesLock.withLock { super.encode(with: coder) coder.encode(aliases, forKey: "aliases") - coder.encode(primaryAliasLabel.rawValue, forKey: "primaryAliasLabel") // Encodes as String } } @@ -83,12 +66,6 @@ class OSIdentityModel: OSModel { // log error return nil } - if let rawType = coder.decodeObject(forKey: "primaryAliasLabel") as? String, - let label = OSDefaultAlias(rawValue: rawType) { - self.primaryAliasLabel = label - } else { - self.primaryAliasLabel = .onesignal_id - } self.aliases = aliases } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index d06b8493c..e5e7f8ead 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -354,6 +354,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { return self.identityModelStore.getModel(modelId: identityModel.modelId) != nil } + func isCurrentUser(_ externalId: String) -> Bool { + guard !externalId.isEmpty else { + OneSignalLog.onesignalLog(.LL_ERROR, message: "isCurrentUser called with empty externalId") + return false + } + + return user.identityModel.externalId == externalId + } /** Clears the existing user's data in preparation for hydration via a fetch user call. */ diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift index ace4ce0bc..52d4d0cfa 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift @@ -39,10 +39,9 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { // requires a `onesignal_id` to send this request func prepareForExecution() -> Bool { - let aliasLabel = identityModel.primaryAliasLabel - if let aliasId = identityModel.primaryAliasId, let appId = OneSignalConfigManager.getAppId() { + if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)/identity" + self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)/identity" return true } else { // self.path is non-nil, so set to empty string diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift index 49986ae36..e46870b2c 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift @@ -44,10 +44,9 @@ class OSRequestCreateSubscription: OneSignalRequest, OSUserRequest { // Need the onesignal_id of the user func prepareForExecution() -> Bool { - let aliasLabel = identityModel.primaryAliasLabel - if let aliasId = identityModel.primaryAliasId, let appId = OneSignalConfigManager.getAppId() { + if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)/subscriptions" + self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)/subscriptions" return true } else { self.path = "" // self.path is non-nil, so set to empty string diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift index dbf8ed914..4532f44e5 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift @@ -30,7 +30,8 @@ import OneSignalCore /** This request will be made with the minimum information needed. The payload will contain an externalId or no identities. The push subscription may or may not have a token or suscriptionId already. - There will be no properties sent. + This request is used for typical User Create, which will include properties and the push subscription, + or to hydrate OneSignal ID for a given External ID, which will only contain the Identity object in the payload. */ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { var sentToClient = false @@ -40,7 +41,7 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { } var identityModel: OSIdentityModel - var pushSubscriptionModel: OSSubscriptionModel + var pushSubscriptionModel: OSSubscriptionModel? var originalPushToken: String? func prepareForExecution() -> Bool { @@ -55,8 +56,11 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { return true } - // When reading from the cache, update the push subscription model + // When reading from the cache, update the push subscription model, if appropriate func updatePushSubscriptionModel(_ pushSubscriptionModel: OSSubscriptionModel) { + guard self.pushSubscriptionModel != nil else { + return + } self.pushSubscriptionModel = pushSubscriptionModel self.parameters?["subscriptions"] = [pushSubscriptionModel.jsonRepresentation()] self.originalPushToken = pushSubscriptionModel.address @@ -89,36 +93,43 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { self.method = POST } + init(aliasLabel: String, aliasId: String, identityModel: OSIdentityModel) { + self.identityModel = identityModel + self.stringDescription = "" + super.init() + self.parameters = [ + "identity": [aliasLabel: aliasId], + "refresh_device_metadata": true, + ] + self.method = POST + } + func encode(with coder: NSCoder) { coder.encode(identityModel, forKey: "identityModel") coder.encode(pushSubscriptionModel, forKey: "pushSubscriptionModel") coder.encode(originalPushToken, forKey: "originalPushToken") coder.encode(parameters, forKey: "parameters") coder.encode(method.rawValue, forKey: "method") // Encodes as String - coder.encode(path, forKey: "path") coder.encode(timestamp, forKey: "timestamp") } required init?(coder: NSCoder) { guard let identityModel = coder.decodeObject(forKey: "identityModel") as? OSIdentityModel, - let pushSubscriptionModel = coder.decodeObject(forKey: "pushSubscriptionModel") as? OSSubscriptionModel, let parameters = coder.decodeObject(forKey: "parameters") as? [String: Any], let rawMethod = coder.decodeObject(forKey: "method") as? UInt32, - let path = coder.decodeObject(forKey: "path") as? String, let timestamp = coder.decodeObject(forKey: "timestamp") as? Date else { // Log error return nil } self.identityModel = identityModel - self.pushSubscriptionModel = pushSubscriptionModel + self.pushSubscriptionModel = coder.decodeObject(forKey: "pushSubscriptionModel") as? OSSubscriptionModel self.originalPushToken = coder.decodeObject(forKey: "originalPushToken") as? String self.stringDescription = "" super.init() self.parameters = parameters self.method = HTTPMethod(rawValue: rawMethod) - self.path = path self.timestamp = timestamp } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift index e8911ca5b..cd12416b3 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift @@ -28,8 +28,8 @@ import OneSignalCore /** - If an alias is passed in, it will be used to fetch the user. If not, then by default, use the `onesignal_id` in the `identityModel` to fetch the user. - The `identityModel` is also used to reference the user that is updated with the response. + Fetch the user by the provided alias. This is expected to be `onesignal_id` in most cases. + The `identityModel` is used to reference the user that is updated with the response. */ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { var sentToClient = false diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestIdentifyUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestIdentifyUser.swift index 320d78b6e..5572efe05 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestIdentifyUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestIdentifyUser.swift @@ -48,15 +48,14 @@ class OSRequestIdentifyUser: OneSignalRequest, OSUserRequest { // requires a onesignal_id to send this request func prepareForExecution() -> Bool { - let aliasLabel = identityModelToIdentify.primaryAliasLabel - if let aliasId = identityModelToIdentify.primaryAliasId, let appId = OneSignalConfigManager.getAppId() { + if let onesignalId = identityModelToIdentify.onesignalId, let appId = OneSignalConfigManager.getAppId() { self.addJWTHeader(identityModel: identityModelToIdentify) - self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)/identity" + self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)/identity" return true } else { // self.path is non-nil, so set to empty string self.path = "" - OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the Identify User request due to null app ID or null \(aliasLabel) ID.") + OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the Identify User request due to null app ID or null OneSignal ID.") return false } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestRemoveAlias.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestRemoveAlias.swift index ac4343dde..49e6ea691 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestRemoveAlias.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestRemoveAlias.swift @@ -38,10 +38,9 @@ class OSRequestRemoveAlias: OneSignalRequest, OSUserRequest { var identityModel: OSIdentityModel func prepareForExecution() -> Bool { - let aliasLabel = identityModel.primaryAliasLabel - if let aliasId = identityModel.primaryAliasId, let appId = OneSignalConfigManager.getAppId() { + if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)/identity/\(labelToRemove)" + self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)/identity/\(labelToRemove)" return true } else { // self.path is non-nil, so set to empty string diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestTransferSubscription.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestTransferSubscription.swift index e9040e5ae..854f12ce9 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestTransferSubscription.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestTransferSubscription.swift @@ -28,80 +28,36 @@ import OneSignalCore /** + Deprecated as of `5.2.3`. Use CreateUser instead. This class skeleton remains due to potentially cached requests. + When this request is uncached, it will be translated to a CreateUser request, if appropriate. + ------- Transfers the Subscription specified by the subscriptionId to the User identified by the identity in the payload. Only one entry is allowed, `onesignal_id` or an Alias. We will use the alias specified. The anticipated usage of this request is only for push subscriptions. */ +@available(*, deprecated, message: "Replaced by Create User") class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { var sentToClient = false - let stringDescription: String - override var description: String { - return stringDescription - } - var subscriptionModel: OSSubscriptionModel let aliasLabel: String let aliasId: String - // Need an alias and subscription_id func prepareForExecution() -> Bool { - if let subscriptionId = subscriptionModel.subscriptionId, let appId = OneSignalConfigManager.getAppId() { - self.path = "apps/\(appId)/subscriptions/\(subscriptionId)/owner" - // TODO: self.addJWTHeader(identityModel: identityModel) ?? - return true - } else { - self.path = "" // self.path is non-nil, so set to empty string - return false - } + return false } - /** - Must pass an Alias pair to identify the User. - */ - init( - subscriptionModel: OSSubscriptionModel, - aliasLabel: String, - aliasId: String - ) { - self.subscriptionModel = subscriptionModel - self.aliasLabel = aliasLabel - self.aliasId = aliasId - self.stringDescription = "" - super.init() - self.parameters = ["identity": [aliasLabel: aliasId]] - self.method = PATCH - _ = prepareForExecution() // sets the path property - } - - func encode(with coder: NSCoder) { - coder.encode(subscriptionModel, forKey: "subscriptionModel") - coder.encode(aliasLabel, forKey: "aliasLabel") - coder.encode(aliasId, forKey: "aliasId") - coder.encode(parameters, forKey: "parameters") - coder.encode(method.rawValue, forKey: "method") // Encodes as String - coder.encode(timestamp, forKey: "timestamp") - } + func encode(with coder: NSCoder) { } + /// All cached instances should have External ID as the alias required init?(coder: NSCoder) { guard - let subscriptionModel = coder.decodeObject(forKey: "subscriptionModel") as? OSSubscriptionModel, let aliasLabel = coder.decodeObject(forKey: "aliasLabel") as? String, - let aliasId = coder.decodeObject(forKey: "aliasId") as? String, - let rawMethod = coder.decodeObject(forKey: "method") as? UInt32, - let parameters = coder.decodeObject(forKey: "parameters") as? [String: Any], - let timestamp = coder.decodeObject(forKey: "timestamp") as? Date + let aliasId = coder.decodeObject(forKey: "aliasId") as? String else { // Log error return nil } - self.subscriptionModel = subscriptionModel self.aliasLabel = aliasLabel self.aliasId = aliasId - self.stringDescription = "" - super.init() - self.parameters = parameters - self.method = HTTPMethod(rawValue: rawMethod) - self.timestamp = timestamp - _ = prepareForExecution() } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift index 307967111..83146ab33 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift @@ -39,12 +39,11 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { // TODO: Decide if addPushSubscriptionIdToAdditionalHeadersIfNeeded should block. // Note Android adds it to requests, if the push sub ID exists func prepareForExecution() -> Bool { - let aliasLabel = identityModel.primaryAliasLabel - if let aliasId = identityModel.primaryAliasId, + if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { _ = self.addPushSubscriptionIdToAdditionalHeaders() self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)" + self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)" return true } else { // self.path is non-nil, so set to empty string diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift index 10b38f4e8..403fe3dd4 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift @@ -94,7 +94,7 @@ extension MockUserRequests { response: userResponse ) client.setMockResponseForRequest( - request: "", + request: "", response: userResponse ) } @@ -111,6 +111,16 @@ extension MockUserRequests { request: "", error: NSError(domain: "not-important", code: 409) ) + // 2. Set the response for the subsequent Create User request + let userResponse = MockUserRequests.testIdentityPayload(onesignalId: osid, externalId: externalId) + client.setMockResponseForRequest( + request: "", + response: userResponse) + // 3. Set the response for the subsequent Fetch User request + client.setMockResponseForRequest( + request: "", + response: fetchResponse + ) } else { // The Identify User is successful, the OSID is unchanged osid = anonUserOSID @@ -119,12 +129,12 @@ extension MockUserRequests { request: "", response: fetchResponse ) + // 2. Set the response for the subsequent Fetch User request + client.setMockResponseForRequest( + request: "", + response: fetchResponse + ) } - // 2. Set the response for the subsequent Fetch User request - client.setMockResponseForRequest( - request: "", - response: fetchResponse - ) } /** @@ -147,7 +157,7 @@ extension MockUserRequests { ] ] client.setMockResponseForRequest( - request: "", + request: "", response: fetchResponse ) } @@ -207,11 +217,4 @@ extension MockUserRequests { response: response ) } - - public static func setTransferSubscriptionResponse(with client: MockOneSignalClient, externalId: String) { - client.setMockResponseForRequest( - request: "", - response: [:] // The SDK does not use the response - ) - } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift index 72782423a..ae30a48c1 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift @@ -43,7 +43,6 @@ public class OneSignalUserMocks: NSObject { public static func resetStaticUserExecutor() { OSUserExecutor.userRequestQueue.removeAll() - OSUserExecutor.transferSubscriptionRequestQueue.removeAll() } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift b/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift index 7d2cd01a7..8769dc3cf 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift @@ -312,7 +312,7 @@ final class OneSignalUserTests: XCTestCase { // Increase flush interval to allow all the updates to batch OSOperationRepo.sharedInstance.pollIntervalMilliseconds = 300 - + /* When */ OneSignalUserManagerImpl.sharedInstance.sendSessionTime(100) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserTests/SwitchUserIntegrationTests.swift b/iOS_SDK/OneSignalSDK/OneSignalUserTests/SwitchUserIntegrationTests.swift index 64073e7c2..f4ef0a632 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUserTests/SwitchUserIntegrationTests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUserTests/SwitchUserIntegrationTests.swift @@ -105,7 +105,6 @@ final class SwitchUserIntegrationTests: XCTestCase { MockUserRequests.setAddTagsAndLanguageResponse(with: client, tags: tagsUserA, language: "lang_a") MockUserRequests.setAddAliasesResponse(with: client, aliases: ["alias_a": "id_a"]) MockUserRequests.setAddEmailResponse(with: client, email: "email_a@example.com") - MockUserRequests.setTransferSubscriptionResponse(with: client, externalId: userA_EUID) // Returns mocked user data to test hydration MockUserRequests.setDefaultFetchUserResponseForHydration(with: client, externalId: userA_EUID) @@ -160,7 +159,6 @@ final class SwitchUserIntegrationTests: XCTestCase { contains: ["subscription": ["token": "email_a@example.com"]]) ) XCTAssertTrue(client.hasExecutedRequestOfType(OSRequestFetchUser.self)) - XCTAssertTrue(client.hasExecutedRequestOfType(OSRequestTransferSubscription.self)) // 3. Asserts for User A - local data is updated via hydration XCTAssertEqual("remote_language", OneSignalUserManagerImpl.sharedInstance.user.propertiesModel.language) @@ -262,15 +260,15 @@ final class SwitchUserIntegrationTests: XCTestCase { // 2. Asserts for User A XCTAssertTrue(client.onlyOneRequest( // Tag + Language - contains: "apps/test-app-id/users/by/external_id/\(userA_EUID)", + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)", contains: ["properties": ["language": "lang_a", "tags": tagsUserA]]) ) XCTAssertTrue(client.onlyOneRequest( // Alias - contains: "apps/test-app-id/users/by/external_id/\(userA_EUID)/identity", + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)/identity", contains: ["identity": ["alias_a": "id_a"]]) ) XCTAssertTrue(client.onlyOneRequest( // Email - contains: "apps/test-app-id/users/by/external_id/\(userA_EUID)/subscriptions", + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)/subscriptions", contains: ["subscription": ["token": "email_a@example.com"]]) ) @@ -388,15 +386,15 @@ final class SwitchUserIntegrationTests: XCTestCase { // 2. Asserts for User A XCTAssertTrue(client.onlyOneRequest( // Tag + Language - contains: "apps/test-app-id/users/by/external_id/\(userA_EUID)", + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)", contains: ["properties": ["language": "lang_a", "tags": tagsUserA]]) ) XCTAssertTrue(client.onlyOneRequest( // Alias - contains: "apps/test-app-id/users/by/external_id/\(userA_EUID)/identity", + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)/identity", contains: ["identity": ["alias_a": "id_a"]]) ) XCTAssertTrue(client.onlyOneRequest( // Email - contains: "apps/test-app-id/users/by/external_id/\(userA_EUID)/subscriptions", + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)/subscriptions", contains: ["subscription": ["token": "email_a@example.com"]]) )