From 2d241bb70bb2ab07cfa7e6f21eaae385ea040df1 Mon Sep 17 00:00:00 2001 From: Nan Date: Wed, 19 Jul 2023 04:13:34 -0700 Subject: [PATCH 1/2] Make `OSResponseStatusType` enum to categorize status codes * Borrow from Android and introduce 5 categories of server responses: - Unauthorized - Missing - Conflict - Retryable --- .../OneSignalCore/Source/API/OSNetworkingUtils.h | 9 +++++++++ .../OneSignalCore/Source/API/OSNetworkingUtils.m | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.h index 585f8278b..795f87be5 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.h @@ -29,9 +29,18 @@ // NS_ASSUME_NONNULL_BEGIN +typedef NS_ENUM(NSInteger, OSResponseStatusType) { + OSResponseStatusInvalid = 0, + OSResponseStatusRetryable, + OSResponseStatusUnauthorized, + OSResponseStatusMissing, + OSResponseStatusConflict +}; + @interface OSNetworkingUtils : NSObject + (NSNumber*)getNetType; ++ (OSResponseStatusType)getResponseStatusType:(NSInteger)statusCode; @end diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.m index b64a23b10..68d9384dd 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSNetworkingUtils.m @@ -38,4 +38,18 @@ + (NSNumber *)getNetType { return @1; } ++ (OSResponseStatusType)getResponseStatusType:(NSInteger)statusCode { + if (statusCode == 400 || statusCode == 402) { + return OSResponseStatusInvalid; + } else if (statusCode == 401 || statusCode == 403) { + return OSResponseStatusUnauthorized; + } else if (statusCode == 404 || statusCode == 410) { + return OSResponseStatusMissing; + } else if (statusCode == 409) { + return OSResponseStatusConflict; + } else { + return OSResponseStatusRetryable; + } +} + @end From 57e4b8092d3ae6148d2b7fd7ad57c5c4739b6da0 Mon Sep 17 00:00:00 2001 From: Nan Date: Wed, 19 Jul 2023 04:15:19 -0700 Subject: [PATCH 2/2] logout when the user no longer exists on server * Use OSResponseStatusType to categorize server error responses. * When we receive a missing status code, we will perform a logout in the SDK and start with a fresh blank anonymous user. But only if the SDK user is still the same one, otherwise, do nothing. --- .../Source/OSIdentityOperationExecutor.swift | 50 ++++++++--- .../Source/OSPropertyOperationExecutor.swift | 27 ++++-- .../OSSubscriptionOperationExecutor.swift | 51 +++++++---- .../OneSignalUser/Source/OSUserRequests.swift | 90 ++++++++++++------- .../Source/OneSignalUserManagerImpl.swift | 4 + 5 files changed, 153 insertions(+), 69 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift index e261d47cd..bfeb61db7 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift @@ -180,14 +180,35 @@ class OSIdentityOperationExecutor: OSOperationExecutor { self.addRequestQueue.removeAll(where: { $0 == request}) OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) } onFailure: { error in - // TODO: What happened, maybe alias exists on another user OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor add aliases request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - self.addRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType == .missing { + // Remove from cache and queue + self.addRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + // Logout if the user in the SDK is the same + guard OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModel) + else { + return + } + // The subscription has been deleted along with the user, so remove the subscription_id but keep the same push subscription model + OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil + OneSignalUserManagerImpl.sharedInstance._logout() + } else if responseType == .conflict { + self.addRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + guard OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModel) + else { + return + } + // Alias(es) already exists on another user, remove from identity model + OneSignalUserManagerImpl.sharedInstance.user.identityModel.removeAliases(Array(request.aliases.keys)) + } else if responseType != .retryable { + // Fail, no retry, remove from cache and queue + self.addRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + } } } } @@ -210,12 +231,15 @@ class OSIdentityOperationExecutor: OSOperationExecutor { OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor remove alias request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - self.removeRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) + + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType != .retryable { + // Fail, no retry, remove from cache and queue + // A response of .missing could mean the alias doesn't exist on this user OR this user has been deleted + self.removeRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) + } } } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift index 5c49cabda..6d2062108 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift @@ -135,17 +135,30 @@ class OSPropertyOperationExecutor: OSOperationExecutor { OneSignalClient.shared().execute(request) { _ in // On success, remove request from cache, and we do need to hydrate - // TODO: We need to hydrate after all + // TODO: We need to hydrate after all ? What why ? self.updateRequestQueue.removeAll(where: { $0 == request}) OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSPropertyOperationExecutor update properties request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - self.updateRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType == .missing { + // remove from cache and queue + self.updateRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) + // Logout if the user in the SDK is the same + guard OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModel) + else { + return + } + // The subscription has been deleted along with the user, so remove the subscription_id but keep the same push subscription model + OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil + OneSignalUserManagerImpl.sharedInstance._logout() + } else if responseType != .retryable { + // Fail, no retry, remove from cache and queue + self.updateRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) + } } } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift index c728e585a..f79ced992 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift @@ -246,12 +246,24 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { request.subscriptionModel.hydrate(response) } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor create subscription request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - self.addRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType == .missing { + self.addRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + // Logout if the user in the SDK is the same + guard OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModel) + else { + return + } + // The subscription has been deleted along with the user, so remove the subscription_id but keep the same push subscription model + OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil + OneSignalUserManagerImpl.sharedInstance._logout() + } else if responseType != .retryable { + // Fail, no retry, remove from cache and queue + self.addRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + } } } } @@ -276,12 +288,14 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor delete subscription request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - self.removeRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType != .retryable { + // Fail, no retry, remove from cache and queue + // If this request returns a missing status, that is ok as this is a delete request + self.removeRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) + } } } } @@ -306,12 +320,13 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor update subscription request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - self.updateRequestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType != .retryable { + // Fail, no retry, remove from cache and queue + self.updateRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) + } } } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift index ca762e08d..b1ce9af89 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift @@ -330,11 +330,13 @@ class OSUserExecutor { } } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor create user request failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - removeFromQueue(request) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType != .retryable { + // Fail, no retry, remove from cache and queue + // TODO: This leaves the SDK in a bad state, revisit why this can happen + removeFromQueue(request) + } } executePendingRequests() } @@ -378,17 +380,14 @@ class OSUserExecutor { } } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor executeFetchIdentityBySubscriptionRequest failed with error: \(error.debugDescription)") - - // TODO: Differentiate error cases - - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - // remove the subscription_id but keep the same push subscription model - OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil - removeFromQueue(request) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType != .retryable { + // Fail, no retry, remove the subscription_id but keep the same push subscription model + OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil + removeFromQueue(request) + } } - // Otherwise it is a retryable error executePendingRequests() } } @@ -428,10 +427,11 @@ class OSUserExecutor { } } onFailure: { error in OneSignalLog.onesignalLog(.LL_VERBOSE, message: "executeIdentifyUserRequest failed with error \(error.debugDescription)") - // Returns 409 if any provided (label, id) pair exists on another User, so the SDK will switch to this user. if let nsError = error as? NSError { - if nsError.code == 409 { - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "executeIdentifyUserRequest returned 409, failed due to alias already assigned to a different user. Now switch to this user.") + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType == .conflict { + // Returns 409 if any provided (label, id) pair exists on another User, so the SDK will switch to this user. + OneSignalLog.onesignalLog(.LL_DEBUG, message: "executeIdentifyUserRequest returned error code user-2. Now handling user-2 error response... switch to this user.") removeFromQueue(request) // Fetch the user only if its the current user @@ -440,9 +440,21 @@ class OSUserExecutor { // TODO: Link ^ to the new user... what was this todo for? } transferPushSubscriptionTo(aliasLabel: request.aliasLabel, aliasId: request.aliasId) - } else if nsError.code < 500 && nsError.code != 0 { + } else if responseType == .invalid || responseType == .unauthorized { + // Failed, no retry removeFromQueue(request) executePendingRequests() + } else if responseType == .missing { + removeFromQueue(request) + executePendingRequests() + // Logout if the user in the SDK is the same + guard OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModelToUpdate) + else { + return + } + // The subscription has been deleted along with the user, so remove the subscription_id but keep the same push subscription model + OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil + OneSignalUserManagerImpl.sharedInstance._logout() } } else { executePendingRequests() @@ -481,12 +493,12 @@ class OSUserExecutor { } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor executeTransferPushSubscriptionRequest failed with error: \(error.debugDescription)") - - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - removeFromQueue(request) + 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() } @@ -520,11 +532,22 @@ class OSUserExecutor { executePendingRequests() } onFailure: { error in OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor executeFetchUserRequest failed with error: \(error.debugDescription)") - // TODO: Differentiate error cases - // If the error is not retryable, remove from cache and queue - if let nsError = error as? NSError, - nsError.code < 500 && nsError.code != 0 { - removeFromQueue(request) + if let nsError = error as? NSError { + let responseType = OSNetworkingUtils.getResponseStatusType(nsError.code) + if responseType == .missing { + removeFromQueue(request) + // Logout if the user in the SDK is the same + guard OneSignalUserManagerImpl.sharedInstance.isCurrentUser(request.identityModel) + else { + return + } + // The subscription has been deleted along with the user, so remove the subscription_id but keep the same push subscription model + OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY]?.subscriptionId = nil + OneSignalUserManagerImpl.sharedInstance._logout() + } else if responseType != .retryable { + // If the error is not retryable, remove from cache and queue + removeFromQueue(request) + } } executePendingRequests() } @@ -872,7 +895,8 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { } var identityModel: OSIdentityModel - + let aliases: [String: String] + // requires a `onesignal_id` to send this request func prepareForExecution() -> Bool { if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { @@ -888,6 +912,7 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { init(aliases: [String: String], identityModel: OSIdentityModel) { self.identityModel = identityModel + self.aliases = aliases self.stringDescription = "OSRequestAddAliases with aliases: \(aliases)" super.init() self.parameters = ["identity": aliases] @@ -897,6 +922,7 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { func encode(with coder: NSCoder) { coder.encode(identityModel, forKey: "identityModel") + coder.encode(aliases, forKey: "aliases") coder.encode(parameters, forKey: "parameters") coder.encode(method.rawValue, forKey: "method") // Encodes as String coder.encode(timestamp, forKey: "timestamp") @@ -905,6 +931,7 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { required init?(coder: NSCoder) { guard let identityModel = coder.decodeObject(forKey: "identityModel") as? OSIdentityModel, + let aliases = coder.decodeObject(forKey: "aliases") as? [String: String], let rawMethod = coder.decodeObject(forKey: "method") as? UInt32, let parameters = coder.decodeObject(forKey: "parameters") as? [String: [String: String]], let timestamp = coder.decodeObject(forKey: "timestamp") as? Date @@ -913,6 +940,7 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { return nil } self.identityModel = identityModel + self.aliases = aliases self.stringDescription = "OSRequestAddAliases with parameters: \(parameters)" super.init() self.parameters = parameters diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index e2221f3d3..b84760068 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -359,6 +359,10 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { OneSignalLog.onesignalLog(.LL_DEBUG, message: "OneSignal.User logout called, but the user is currently anonymous, so not logging out.") return } + _logout() + } + + public func _logout() { prepareForNewUser() _user = nil createUserIfNil()