From a106bebd696ce81efc7495ea4583384878f17ece Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 30 Jan 2023 21:38:12 -0800 Subject: [PATCH 01/27] Retry requests with a backoff strategy & keep max retries * Keep max retries for now so we don't spam the server with requests when it is in trouble to avoid retrying forever. * Let a request retry 4 times (so total 5 times) with backoff strategy calculated with multiples of 3. * Remove some no longer used tag delay variables from common defines --- .../OneSignalCore/Source/API/OneSignalClient.m | 16 +++++++++++----- .../Source/OneSignalCommonDefines.h | 10 ++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m index 16b127cd9..2ee0123c6 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m @@ -59,8 +59,8 @@ -(instancetype)init { - (NSURLSessionConfiguration *)configurationWithCachingPolicy:(NSURLRequestCachePolicy)policy { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - configuration.timeoutIntervalForRequest = REQUEST_TIMEOUT_REQUEST; - configuration.timeoutIntervalForResource = REQUEST_TIMEOUT_RESOURCE; + configuration.timeoutIntervalForRequest = REQUEST_TIMEOUT_REQUEST; // TODO: Are these anything? + configuration.timeoutIntervalForResource = REQUEST_TIMEOUT_RESOURCE; // TODO: Are these anything? //prevent caching of requests, this mainly impacts OSRequestGetIosParams, //since the OSRequestGetTags endpoint has a caching header policy @@ -333,10 +333,11 @@ - (BOOL)willReattemptRequest:(int)statusCode withRequest:(OneSignalRequest *)req OSReattemptRequest *reattempt = [OSReattemptRequest withRequest:request successBlock:successBlock failureBlock:failureBlock]; if (async) { - //retry again in 15 seconds - [OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"Re-scheduling request (%@) to be re-attempted in %.3f seconds due to failed HTTP request with status code %i", NSStringFromClass([request class]), REATTEMPT_DELAY, (int)statusCode]]; + //retry again in an increasing interval calculated with reattemptDelay + double reattemptDelay = [self calculateReattemptDelay:request.reattemptCount]; + [OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"Re-scheduling request (%@) to be re-attempted in %.3f seconds due to failed HTTP request with status code %i", NSStringFromClass([request class]), reattemptDelay, (int)statusCode]]; [OneSignalCoreHelper dispatch_async_on_main_queue:^{ - [self performSelector:@selector(reattemptRequest:) withObject:reattempt afterDelay:REATTEMPT_DELAY]; + [self performSelector:@selector(reattemptRequest:) withObject:reattempt afterDelay:reattemptDelay]; }]; } else { //retry again immediately @@ -349,6 +350,11 @@ - (BOOL)willReattemptRequest:(int)statusCode withRequest:(OneSignalRequest *)req return false; } +// A request will retry with intervals of 5, 15 , 45, 135 seconds... +- (double)calculateReattemptDelay:(int)reattemptCount { + return REATTEMPT_DELAY * pow(3, reattemptCount); +} + - (void)prettyPrintDebugStatementWithRequest:(OneSignalRequest *)request { if (![NSJSONSerialization isValidJSONObject:request.parameters]) return; diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h index 68165d972..22596e579 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h @@ -231,13 +231,10 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP #ifndef OS_TEST // OneSignal API Client Defines - #define REATTEMPT_DELAY 30.0 + #define REATTEMPT_DELAY 5.0 #define REQUEST_TIMEOUT_REQUEST 120.0 //for most HTTP requests #define REQUEST_TIMEOUT_RESOURCE 120.0 //for loading a resource like an image - #define MAX_ATTEMPT_COUNT 3 - - // Send tags batch delay - #define SEND_TAGS_DELAY 5.0 + #define MAX_ATTEMPT_COUNT 5 // the max number of UNNotificationCategory ID's the SDK will register #define MAX_CATEGORIES_SIZE 128 @@ -254,9 +251,6 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP #define REQUEST_TIMEOUT_RESOURCE 0.02 //for loading a resource like an image #define MAX_ATTEMPT_COUNT 3 - // Send tags batch delay - #define SEND_TAGS_DELAY 0.005 - // the max number of UNNotificationCategory ID's the SDK will register #define MAX_CATEGORIES_SIZE 5 From 6ae3d565098880233c2203f077cb8892efb83db2 Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 30 Jan 2023 22:56:17 -0800 Subject: [PATCH 02/27] Client will pass along status code even when successful * When a request is successful, we were not passing along the status code to the success callback. * Let's pass along the status code in the information about a request when it is successful. --- .../OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m index 2ee0123c6..9c38f0c7f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m @@ -381,7 +381,8 @@ - (void)handleJSONNSURLResponse:(NSURLResponse*)response data:(NSData*)data erro NSMutableDictionary* innerJson; if (data != nil && [data length] > 0) { - innerJson = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; + innerJson = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; + innerJson[@"httpStatusCode"] = [NSNumber numberWithLong:statusCode]; [OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"network response (%@): %@", NSStringFromClass([request class]), innerJson]]; if (jsonError) { if (failureBlock != nil) From 234fbdde0ad89e30018e16f2c5b8d9c365b34cea Mon Sep 17 00:00:00 2001 From: Nan Date: Tue, 31 Jan 2023 02:11:26 -0800 Subject: [PATCH 03/27] Updates to `OSUserExecutor` * In `start()`, initialize from cache and hook models up. To accomplish this, we maintain a dictionary of seen identity models. * Add error handling, with caching and removing from cache when request is successful or return an unretryable error. * Requests will also maintain a flag called `sentToClient` to indicate if this request has been sent, and should not be flushed. * Some request had extraneous fields so those are removed. user execuor exeuctor user user exe --- .../Source/OneSignalCommonDefines.h | 3 +- .../OneSignalUser/Source/OSUserRequests.swift | 281 ++++++++++++++---- 2 files changed, 232 insertions(+), 52 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h index 22596e579..e6032863c 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h @@ -304,7 +304,8 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP #define OS_OPERATION_REPO_DELTA_QUEUE_KEY @"OS_OPERATION_REPO_DELTA_QUEUE_KEY" // User Executor -#define OS_USER_EXECUTOR_REQUEST_QUEUE_KEY @"OS_USER_EXECUTOR_REQUEST_QUEUE_KEY" +#define OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY @"OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY" +#define OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY @"OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY" // Identity Executor #define OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY @"OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY" diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift index 1017ea1a6..b630e5cb7 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift @@ -31,34 +31,136 @@ import OneSignalOSCore /** Involved in the login process and responsible for Identify User and Create User. + Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestTransferSubscription`, `OSRequestFetchUser`. */ class OSUserExecutor { - static var requestQueue: [OSUserRequest] = [] + static var userRequestQueue: [OSUserRequest] = [] + static var transferSubscriptionRequestQueue: [OSRequestTransferSubscription] = [] + static var identityModels: [String: OSIdentityModel] = [:] + // Read in requests from the cache, do not read in FetchUser requests as this is not needed. static func start() { - // Read unfinished requests from cache, if any... - if let requestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_USER_EXECUTOR_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSUserRequest] { - self.requestQueue = requestQueue + var userRequestQueue: [OSUserRequest] = [] + + // Read unfinished Create User + Identify User requests from cache, if any... + if let cachedRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSUserRequest] { + // Hook each uncached Request to the right model reference + for request in cachedRequestQueue { + if request.isKind(of: OSRequestCreateUser.self), let req = request as? OSRequestCreateUser { + if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: req.identityModel.modelId) { + // 1. The model exist in the store, set it to be the Request's model + req.identityModel = identityModel + } else if let identityModel = identityModels[req.identityModel.modelId] { + // 2. The model exists in the dict of identityModels already processed to use + req.identityModel = identityModel + } else { + // 3. The models do not exist, use the model on the request, and add to dict. + identityModels[req.identityModel.modelId] = req.identityModel + } + userRequestQueue.append(req) + + } else if request.isKind(of: OSRequestIdentifyUser.self), let req = request as? OSRequestIdentifyUser { + + if let identityModelToIdentify = identityModels[req.identityModelToIdentify.modelId], + let identityModelToUpdate = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: req.identityModelToUpdate.modelId) { + // 1. A model exist in the dict and a model exist in the store, set it to be the Request's models + req.identityModelToIdentify = identityModelToIdentify + req.identityModelToUpdate = identityModelToUpdate + } else if let identityModelToIdentify = identityModels[req.identityModelToIdentify.modelId], + let identityModelToUpdate = identityModels[req.identityModelToUpdate.modelId] { + // 2. The two models exist in the dict, set it to be the Request's models + req.identityModelToIdentify = identityModelToIdentify + req.identityModelToUpdate = identityModelToUpdate + } else if let identityModelToIdentify = identityModels[req.identityModelToIdentify.modelId], + identityModels[req.identityModelToUpdate.modelId] == nil { + // 3. A model is in the dict, the other model does not exist + req.identityModelToIdentify = identityModelToIdentify + identityModels[req.identityModelToUpdate.modelId] = req.identityModelToUpdate + } else { + // 4. Both models don't exist yet + identityModels[req.identityModelToIdentify.modelId] = req.identityModelToIdentify + identityModels[req.identityModelToUpdate.modelId] = req.identityModelToUpdate + } + userRequestQueue.append(req) + } + } + } + self.userRequestQueue = 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() reading request \(request) from cache failed. Dropping request.") + self.transferSubscriptionRequestQueue = [] + } + } + } else { + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor error encountered reading from cache for \(OS_USER_EXECUTOR_TRANSFER_SUBSCRIPTION_REQUEST_QUEUE_KEY)") + } + + executePendingRequests() + } + + static func appendToQueue(_ request: OSUserRequest) { + 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) + } + } + + static func removeFromQueue(_ request: OSUserRequest) { + 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 { - // log error + userRequestQueue.removeAll(where: { $0 == request}) + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) } } static func executePendingRequests() { - for request in requestQueue { + let requestQueue: [OSUserRequest] = userRequestQueue + transferSubscriptionRequestQueue + + if requestQueue.isEmpty { + return + } + + // Sort the requestQueue by timestamp + for request in requestQueue.sorted(by: { first, second in + return first.timestamp < second.timestamp + }) { // Return as soon as we reach an un-executable request if !request.prepareForExecution() { return } - // This request is Identify User - if request.isKind(of: OSRequestIdentifyUser.self), let identifyUserRequest = request as? OSRequestIdentifyUser { + + if request.isKind(of: OSRequestCreateUser.self), let createUserRequest = request as? OSRequestCreateUser { + executeCreateUserRequest(createUserRequest) + return + } 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 } else { // Log Error + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor met incompatible Request type that cannot be executed.") } - // Remove the request from queue and cache - requestQueue.removeAll(where: { $0 == request}) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_REQUEST_QUEUE_KEY, withValue: self.requestQueue) } } @@ -167,31 +269,58 @@ class OSUserExecutor { // We will pass minimal properties to this request static func createUser(_ user: OSUserInternal) { let originalPushToken = user.pushSubscriptionModel.address - let request = OSRequestCreateUser(identityModel: user.identityModel, pushSubscriptionModel: user.pushSubscriptionModel) + let request = OSRequestCreateUser(identityModel: user.identityModel, propertiesModel: user.propertiesModel, pushSubscriptionModel: user.pushSubscriptionModel, originalPushToken: originalPushToken) + + appendToQueue(request) + + executePendingRequests() + } - // Currently there are no requirements needed before sending this request + static func executeCreateUserRequest(_ request: OSRequestCreateUser) { + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { + // Currently there are no requirements needed before sending this request 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) { + request.pushSubscriptionModel = pushSubscriptionModel + request.updatePushSubscriptionModel(pushSubscriptionModel) + } + OneSignalClient.shared().execute(request) { response in + removeFromQueue(request) + // TODO: Differentiate if we need to fetch the user based on response code of 200, 201, 202 // Create User's response won't send us the user's complete info if this user already exists if let response = response { // Parse the response for any data we need to update - parseFetchUserResponse(response: response, identityModel: request.identityModel, originalPushToken: originalPushToken) + parseFetchUserResponse(response: response, identityModel: request.identityModel, originalPushToken: request.originalPushToken) // 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 - if let identity = request.parameters?["identity"] as? [String: String], + // Fetch the user only if its the current user + if let _ = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId), + let identity = request.parameters?["identity"] as? [String: String], let externalId = identity[OS_EXTERNAL_ID] { fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: externalId, identityModel: request.identityModel) + } else { + executePendingRequests() } } - executePendingRequests() } onFailure: { error in - OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSUserExecutor create user request failed with error: \(error.debugDescription)") - // Depending on error, Client is responsible for retrying. - // executePendingRequests() ? + 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) + } + executePendingRequests() } } @@ -203,34 +332,52 @@ class OSUserExecutor { identityModelToUpdate: identityModelToUpdate ) + appendToQueue(request) + + executePendingRequests() + } + + static func executeIdentifyUserRequest(_ request: OSRequestIdentifyUser) { + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { // Missing onesignal_id - // This request still stays in the queue and cache - requestQueue.append(request) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_REQUEST_QUEUE_KEY, withValue: self.requestQueue) return } + request.sentToClient = true - executeIdentifyUserRequest(request) - } - - static func executeIdentifyUserRequest(_ request: OSRequestIdentifyUser) { OneSignalClient.shared().execute(request) { _ in + removeFromQueue(request) + // the anonymous user has been identified, still need to Fetch User as we cleared local data - fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) - executePendingRequests() // TODO: Here or before fetch? + // Fetch the user only if its the current user + if let _ = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModelToUpdate.modelId) { + fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) + } else { + executePendingRequests() + } } onFailure: { error in + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "executeIdentifyUserRequest failed with error \(error.debugDescription)") + removeFromQueue(request) // Returns 409 if any provided (label, id) pair exists on another User, so the SDK will switch to this user. - if error?._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.") - - fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) - // TODO: Link ^ to the new user... what was this todo for? - transferPushSubscriptionTo(aliasLabel: request.aliasLabel, aliasId: request.aliasId, retainPreviousUser: true) // update logic to determine flag - executePendingRequests() // Here or after fetch or after transfer? + 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.") + + removeFromQueue(request) + // Fetch the user only if its the current user + if let _ = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModelToUpdate.modelId) { + fetchUser(aliasLabel: OS_EXTERNAL_ID, aliasId: request.aliasId, identityModel: request.identityModelToUpdate) + // 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 { + removeFromQueue(request) + executePendingRequests() + } } else { - // If not 409, we retry, depending on what the error is? - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "executeIdentifyUserRequest failed without returning a 409.") + executePendingRequests() } } } @@ -241,46 +388,78 @@ class OSUserExecutor { subscriptionModel: OneSignalUserManagerImpl.sharedInstance.user.pushSubscriptionModel, aliasLabel: aliasLabel, aliasId: aliasId, - identityModel: nil, retainPreviousUser: retainPreviousUser // Need to update logic to determine this, for now, default to true ) - guard request.prepareForExecution() else { - // Missing subscriptionId. This request still stays in the queue and cache - requestQueue.append(request) - OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_REQUEST_QUEUE_KEY, withValue: self.requestQueue) - return - } + appendToQueue(request) - executeTransferPushSubscriptionRequest(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 OneSignalClient.shared().execute(request) { _ in + removeFromQueue(request) + // TODO: ... hydrate with returned identity object? executePendingRequests() - } onFailure: { _ in - // TODO: What happened? Client responsible for retrying. + } 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) + } + executePendingRequests() } } static func fetchUser(aliasLabel: String, aliasId: String, identityModel: OSIdentityModel) { let request = OSRequestFetchUser(identityModel: identityModel, aliasLabel: aliasLabel, aliasId: aliasId) + appendToQueue(request) + + executePendingRequests() + } + + static func executeFetchUserRequest(_ request: OSRequestFetchUser) { + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { - // This should not happen as we set the alias to use for the request path, log error + // This should not happen as we set the alias to use for the request path return } - + request.sentToClient = true OneSignalClient.shared().execute(request) { response in + removeFromQueue(request) + if let response = response { // Clear local data in preparation for hydration OneSignalUserManagerImpl.sharedInstance.clearUserData() parseFetchUserResponse(response: response, identityModel: request.identityModel, originalPushToken: OneSignalUserManagerImpl.sharedInstance.token) } - } onFailure: { _ in - // TODO: What? + 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) + } + executePendingRequests() } } } From d95681078ec0a22c63ae35601ae2ab415dc536ac Mon Sep 17 00:00:00 2001 From: Nan Date: Tue, 31 Jan 2023 02:17:42 -0800 Subject: [PATCH 04/27] Update user related requests * Includes updates to add `sentToClient` flag * Includes updates to some parameters to requests to not be optional, and update the prepareForExecution() methods accordingly. * Includes some requests had extraneous fields so those are removed. * Includes passing up language in create user --- .../OneSignalUser/Source/OSUserRequests.swift | 136 +++++++++--------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift index b630e5cb7..91d07e1c4 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift @@ -467,6 +467,7 @@ class OSUserExecutor { // MARK: - User Request Classes protocol OSUserRequest: OneSignalRequest, NSCoding { + var sentToClient: Bool { get set } func prepareForExecution() -> Bool } @@ -479,13 +480,15 @@ protocol OSUserRequest: OneSignalRequest, NSCoding { There will be no properties sent. */ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription } - let identityModel: OSIdentityModel - let pushSubscriptionModel: OSSubscriptionModel + var identityModel: OSIdentityModel + var pushSubscriptionModel: OSSubscriptionModel + let originalPushToken: String? func prepareForExecution() -> Bool { guard let appId = OneSignalConfigManager.getAppId() else { @@ -498,13 +501,10 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { return true } - init(identityModel: OSIdentityModel, pushSubscriptionModel: OSSubscriptionModel) { - self.identityModel = identityModel + // When reading from the cache, update the push subscription model + func updatePushSubscriptionModel(_ pushSubscriptionModel: OSSubscriptionModel) { self.pushSubscriptionModel = pushSubscriptionModel - self.stringDescription = "OSRequestCreateUser" - - super.init() - + // Push Subscription Object var pushSubscriptionObject: [String: Any] = [:] pushSubscriptionObject["id"] = pushSubscriptionModel.subscriptionId pushSubscriptionObject["type"] = pushSubscriptionModel.type.rawValue @@ -520,22 +520,38 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { if pushSubscriptionModel.notificationTypes != -1 { pushSubscriptionObject["notification_types"] = pushSubscriptionModel.notificationTypes } + self.parameters?["subscriptions"] = [pushSubscriptionObject] + } + + init(identityModel: OSIdentityModel, propertiesModel: OSPropertiesModel, pushSubscriptionModel: OSSubscriptionModel, originalPushToken: String?) { + self.identityModel = identityModel + self.pushSubscriptionModel = pushSubscriptionModel + self.originalPushToken = originalPushToken + self.stringDescription = "OSRequestCreateUser" + super.init() var params: [String: Any] = [:] + + // Identity Object params["identity"] = [:] if let externalId = identityModel.externalId { params["identity"] = [OS_EXTERNAL_ID: externalId] } - params["subscriptions"] = [pushSubscriptionObject] - params["properties"] = [:] + + // Properties Object + var propertiesObject: [String: Any] = [:] + propertiesObject["language"] = propertiesModel.language + params["properties"] = propertiesObject self.parameters = params + self.updatePushSubscriptionModel(pushSubscriptionModel) 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") @@ -556,6 +572,7 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { } self.identityModel = identityModel self.pushSubscriptionModel = pushSubscriptionModel + self.originalPushToken = coder.decodeObject(forKey: "originalPushToken") as? String self.stringDescription = "OSRequestCreateUser" super.init() self.parameters = parameters @@ -569,18 +586,18 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { The `identityModelToIdentify` is used for the `onesignal_id` of the user we want to associate with this alias. This request will tell us if we should continue with the previous user who is now identitfied, or to change users to the one this alias already exists on. - Note: The SDK needs an user to operate on before this request returns. However, at the time of this request's creation, the SDK does not know if there is already - an user associated with this alias. So, it creates a blank new user (whose identity model is passed in as `identityModelToUpdate`, + Note: The SDK needs an user to operate on before this request returns. However, at the time of this request's creation, the SDK does not know if there is already an user associated with this alias. So, it creates a blank new user (whose identity model is passed in as `identityModelToUpdate`, which is the model used to make a subsequent ``OSRequestFetchUser``). */ class OSRequestIdentifyUser: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription } - let identityModelToIdentify: OSIdentityModel - let identityModelToUpdate: OSIdentityModel + var identityModelToIdentify: OSIdentityModel + var identityModelToUpdate: OSIdentityModel let aliasLabel: String let aliasId: String @@ -657,40 +674,31 @@ class OSRequestIdentifyUser: OneSignalRequest, OSUserRequest { The `identityModel` is also used to reference the user that is updated with the response. */ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription } let identityModel: OSIdentityModel - let aliasLabel: String? - let aliasId: String? + let aliasLabel: String + let aliasId: String func prepareForExecution() -> Bool { - // If there is an alias, use that - if let aliasLabelToUse = aliasLabel, - let appId = OneSignalConfigManager.getAppId(), - let aliasIdToUse = aliasId { - self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(aliasLabelToUse)/\(aliasIdToUse)" - return true - } - // Otherwise, use the onesignal_id - if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { - self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)" - return true - } else { - // self.path is non-nil, so set to empty string - self.path = "" + guard let appId = OneSignalConfigManager.getAppId() else { + OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the fetch user request due to null app ID.") return false } + self.addJWTHeader(identityModel: identityModel) + self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)" + return true } - init(identityModel: OSIdentityModel, aliasLabel: String?, aliasId: String?) { + init(identityModel: OSIdentityModel, aliasLabel: String, aliasId: String) { self.identityModel = identityModel self.aliasLabel = aliasLabel self.aliasId = aliasId - self.stringDescription = "OSRequestFetchUser with aliasLabel: \(aliasLabel ?? "nil") aliasId: \(aliasId ?? "nil")" + self.stringDescription = "OSRequestFetchUser with aliasLabel: \(aliasLabel) aliasId: \(aliasId)" super.init() self.method = GET _ = prepareForExecution() // sets the path property @@ -707,6 +715,8 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { required init?(coder: NSCoder) { guard let identityModel = coder.decodeObject(forKey: "identityModel") as? OSIdentityModel, + 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 timestamp = coder.decodeObject(forKey: "timestamp") as? Date else { @@ -714,9 +724,9 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { return nil } self.identityModel = identityModel - self.aliasLabel = coder.decodeObject(forKey: "aliasLabel") as? String - self.aliasId = coder.decodeObject(forKey: "aliasId") as? String - self.stringDescription = "OSRequestFetchUser with aliasLabel: \(aliasLabel ?? "nil") aliasId: \(aliasId ?? "nil")" + self.aliasLabel = aliasLabel + self.aliasId = aliasId + self.stringDescription = "OSRequestFetchUser with aliasLabel: \(aliasLabel) aliasId: \(aliasId)" super.init() self.method = HTTPMethod(rawValue: rawMethod) self.timestamp = timestamp @@ -725,6 +735,7 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { } class OSRequestAddAliases: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription @@ -782,6 +793,7 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { } class OSRequestRemoveAlias: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription @@ -839,15 +851,19 @@ class OSRequestRemoveAlias: OneSignalRequest, OSUserRequest { } class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription } // TODO: does updating properties even have a response in which we need to hydrate from? Then we can get rid of modelToUpdate + // Yes we may, if we cleared local state var modelToUpdate: OSPropertiesModel var identityModel: OSIdentityModel + // TODO: Decide if addPushSubscriptionIdToAdditionalHeadersIfNeeded should block. + // Note Android adds it to requests, if the push sub ID exists func prepareForExecution() -> Bool { if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId(), @@ -866,7 +882,7 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { guard let parameters = self.parameters else { return true } - if parameters["deltas"] != nil { + if parameters["deltas"] != nil { // , !parameters["deltas"].isEmpty if let pushSubscriptionId = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId { var additionalHeaders = self.additionalHeaders ?? [String: String]() additionalHeaders["OneSignal-Subscription-Id"] = pushSubscriptionId @@ -931,6 +947,7 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { this request because they will be created with ``OSRequestCreateUser``. */ class OSRequestCreateSubscription: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription @@ -1001,38 +1018,29 @@ class OSRequestCreateSubscription: OneSignalRequest, OSUserRequest { /** 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 not use the identityModel at all if there is an alias specified. + 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. */ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription } - let subscriptionModel: OSSubscriptionModel - let identityModel: OSIdentityModel? - let aliasLabel: String? - let aliasId: String? + 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" // Check alias pair - if let label = aliasLabel, - let id = aliasId { - // parameters should be set in init(), so not optional - self.parameters?["identity"] = [label: id] - return true - } - if let identityModel = identityModel, let onesignalId = identityModel.onesignalId { - self.parameters?["identity"] = [OS_ONESIGNAL_ID: onesignalId] - self.addJWTHeader(identityModel: identityModel) - return true - } else { - return false - } + // parameters should be set in init(), so not optional + self.parameters?["identity"] = [aliasLabel: aliasId] + // TODO: self.addJWTHeader(identityModel: identityModel) ?? + return true } else { self.path = "" // self.path is non-nil, so set to empty string return false @@ -1040,18 +1048,16 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { } /** - Must pass either an `identityModel` or an Alias pair to identify the User. + Must pass an Alias pair to identify the User. If `retainPreviousUser` flag is not passed in, it defaults to `true`. */ init( subscriptionModel: OSSubscriptionModel, - aliasLabel: String?, - aliasId: String?, - identityModel: OSIdentityModel?, + aliasLabel: String, + aliasId: String, retainPreviousUser: Bool? ) { self.subscriptionModel = subscriptionModel - self.identityModel = identityModel self.aliasLabel = aliasLabel self.aliasId = aliasId self.stringDescription = "OSRequestTransferSubscription" @@ -1063,7 +1069,6 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { func encode(with coder: NSCoder) { coder.encode(subscriptionModel, forKey: "subscriptionModel") - coder.encode(identityModel, forKey: "identityModel") coder.encode(aliasLabel, forKey: "aliasLabel") coder.encode(aliasId, forKey: "aliasId") coder.encode(parameters, forKey: "parameters") @@ -1074,6 +1079,8 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { 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 @@ -1082,9 +1089,8 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { return nil } self.subscriptionModel = subscriptionModel - self.identityModel = coder.decodeObject(forKey: "identityModel") as? OSIdentityModel - self.aliasLabel = coder.decodeObject(forKey: "aliasLabel") as? String - self.aliasId = coder.decodeObject(forKey: "aliasId") as? String + self.aliasLabel = aliasLabel + self.aliasId = aliasId self.stringDescription = "OSRequestTransferSubscription" super.init() self.parameters = parameters @@ -1098,6 +1104,7 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { Currently, only the Push Subscription will make this Update Request. */ class OSRequestUpdateSubscription: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription @@ -1176,6 +1183,7 @@ class OSRequestUpdateSubscription: OneSignalRequest, OSUserRequest { - Remark: If this model did not already exist in the store, no request is created. */ class OSRequestDeleteSubscription: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription From c9318e840296fed4fdb3d764f22bf1b3abd0b2bd Mon Sep 17 00:00:00 2001 From: Nan Date: Tue, 31 Jan 2023 02:36:24 -0800 Subject: [PATCH 05/27] remove retain_previous_user flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * No longer accepted by the server when transferring a push subscription. * Server checks anonymous user for whether it has any aliases set. If so, we’ll keep the user. If there aren’t aliases, we’ll remove it. Done behind the scenes in Perseus. --- .../OneSignalCore/Source/OneSignalCommonDefines.h | 1 - .../OneSignalUser/Source/OSUserRequests.swift | 14 ++++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h index e6032863c..882022816 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h @@ -278,7 +278,6 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP #define OS_ONESIGNAL_ID @"onesignal_id" #define OS_EXTERNAL_ID @"external_id" -#define OS_RETAIN_PREVIOUS_USER @"retain_previous_user" #define OS_ON_USER_WILL_CHANGE @"OS_ON_USER_WILL_CHANGE" // Models and Model Stores diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift index 91d07e1c4..a55189c1f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift @@ -382,13 +382,12 @@ class OSUserExecutor { } } - static func transferPushSubscriptionTo(aliasLabel: String, aliasId: String, retainPreviousUser: Bool?) { + 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, - retainPreviousUser: retainPreviousUser // Need to update logic to determine this, for now, default to true + aliasId: aliasId ) appendToQueue(request) @@ -1036,9 +1035,6 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { func prepareForExecution() -> Bool { if let subscriptionId = subscriptionModel.subscriptionId, let appId = OneSignalConfigManager.getAppId() { self.path = "apps/\(appId)/subscriptions/\(subscriptionId)/owner" - // Check alias pair - // parameters should be set in init(), so not optional - self.parameters?["identity"] = [aliasLabel: aliasId] // TODO: self.addJWTHeader(identityModel: identityModel) ?? return true } else { @@ -1049,20 +1045,18 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { /** Must pass an Alias pair to identify the User. - If `retainPreviousUser` flag is not passed in, it defaults to `true`. */ init( subscriptionModel: OSSubscriptionModel, aliasLabel: String, - aliasId: String, - retainPreviousUser: Bool? + aliasId: String ) { self.subscriptionModel = subscriptionModel self.aliasLabel = aliasLabel self.aliasId = aliasId self.stringDescription = "OSRequestTransferSubscription" super.init() - self.parameters = [OS_RETAIN_PREVIOUS_USER: retainPreviousUser ?? true] + self.parameters = ["identity": [aliasLabel: aliasId]] self.method = PATCH _ = prepareForExecution() // sets the path property } From 2e4447ae6c881a9c355f2f3279d75bf695a8ba75 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 00:53:45 -0800 Subject: [PATCH 06/27] [nits] todos, comments --- .../OneSignalCore/Source/API/OneSignalReachability.m | 2 +- .../OneSignalUser/Source/OneSignalUserManagerImpl.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalReachability.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalReachability.m index 1a8b8b63d..bdf196c1b 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalReachability.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalReachability.m @@ -29,7 +29,7 @@ #import #import #import - +// TODO: Before GA: There is a better native way to work with this, using different imports #import #import "OneSignalReachability.h" diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index be2cf1b9b..aaa2f2e39 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -462,6 +462,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { extension OneSignalUserManagerImpl { @objc public func startNewSession() { + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OneSignalUserManagerImpl starting new session") guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } From deb61372d5b4c0fde7cadb34a34b059023a04289 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 00:58:13 -0800 Subject: [PATCH 07/27] re-arrange User Manager startup code order * User executor should run earlier --- .../Source/OneSignalUserManagerImpl.swift | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index aaa2f2e39..d0066b113 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -97,7 +97,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { @objc public var pushSubscriptionId: String? { return _user?.pushSubscriptionModel.subscriptionId } - + @objc public var language: String? { return _user?.propertiesModel.language } @@ -194,14 +194,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OneSignalUserManager.start called, loaded the user from cache.") } - // Creates an anonymous user if there isn't one in the cache - createUserIfNil() - - // Model store listeners subscribe to their models - identityModelStoreListener.start() - propertiesModelStoreListener.start() - subscriptionModelStoreListener.start() - pushSubscriptionModelStoreListener.start() + // TODO: Update the push sub model with any new state from NotificationsManager // Setup the executors OSUserExecutor.start() @@ -217,6 +210,15 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { OSOperationRepo.sharedInstance.addExecutor(identityExecutor) OSOperationRepo.sharedInstance.addExecutor(propertyExecutor) OSOperationRepo.sharedInstance.addExecutor(subscriptionExecutor) + + // Creates an anonymous user if there isn't one in the cache + createUserIfNil() + + // Model store listeners subscribe to their models + identityModelStoreListener.start() + propertiesModelStoreListener.start() + subscriptionModelStoreListener.start() + pushSubscriptionModelStoreListener.start() } @objc From 033dc9dc37d66921caa870443b693e812e0410e4 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 01:02:32 -0800 Subject: [PATCH 08/27] Update the Property Executor * Hook up models from cache to requests when app starting up * Removing requests after success or unretryable failure --- .../Source/OSPropertyOperationExecutor.swift | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift index b68079f07..5c49cabda 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertyOperationExecutor.swift @@ -35,14 +35,15 @@ class OSPropertyOperationExecutor: OSOperationExecutor { init() { // Read unfinished deltas from cache, if any... + // Note that we should only have deltas for the current user as old ones are flushed.. if var deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_PROPERTIES_EXECUTOR_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] { // Hook each uncached Delta to the model in the store for (index, delta) in deltaQueue.enumerated().reversed() { if let modelInStore = OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.getModel(modelId: delta.model.modelId) { - // The model exists in the properties model store, set it to be the Delta's model + // 1. The model exists in the properties model store, set it to be the Delta's model delta.model = modelInStore } else { - // The model does not exist, drop this Delta + // 2. The model does not exist, drop this Delta deltaQueue.remove(at: index) } } @@ -55,13 +56,18 @@ class OSPropertyOperationExecutor: OSOperationExecutor { if var updateRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestUpdateProperties] { // Hook each uncached Request to the model in the store for (index, request) in updateRequestQueue.enumerated().reversed() { - if let propertiesModel = OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.getModel(modelId: request.modelToUpdate.modelId), - let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // The models exist in the stores, set it to be the Request's models + // 0. Hook up the properties model if its the current user's so it can hydrate + if let propertiesModel = OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.getModel(modelId: request.modelToUpdate.modelId) { request.modelToUpdate = propertiesModel + } + if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { + // 1. The identity model exist in the store, set it to be the Request's models + request.identityModel = identityModel + } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { + // 2. The model exists in the user executor request.identityModel = identityModel } else if !request.prepareForExecution() { - // The models do not exist AND this request cannot be sent, drop this Request + // 3. The identitymodel do not exist AND this request cannot be sent, drop this Request updateRequestQueue.remove(at: index) } } @@ -119,21 +125,28 @@ class OSPropertyOperationExecutor: OSOperationExecutor { } func executeUpdatePropertiesRequest(_ request: OSRequestUpdateProperties) { + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { return } - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSPropertyOperationExecutor: executeUpdatePropertiesRequest making request: \(request)") - OneSignalClient.shared().execute(request) { _ in + request.sentToClient = true - // On success, remove request from cache, and hydrate model - // TODO: Do we actually hydrate model though? - // For example, if app restarts and we read in operations between sending this off and getting the response + OneSignalClient.shared().execute(request) { _ in + // On success, remove request from cache, and we do need to hydrate + // TODO: We need to hydrate after all self.updateRequestQueue.removeAll(where: { $0 == request}) OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) - } onFailure: { error in - self.updateRequestQueue.removeAll(where: { $0 == request}) - OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription) + 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) + } } } } From a64ac01e108ba80e7c3c15815ed19c0c6194e9ec Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 01:04:58 -0800 Subject: [PATCH 09/27] Update Identity Executor * Hook up models from cache to requests when app starting up * Removing requests after success or unretryable failure --- .../Source/OSIdentityOperationExecutor.swift | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift index 51dd8e2e0..e261d47cd 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityOperationExecutor.swift @@ -59,10 +59,13 @@ class OSIdentityOperationExecutor: OSOperationExecutor { // Hook each uncached Request to the model in the store for (index, request) in addRequestQueue.enumerated().reversed() { if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // The model exists in the store, set it to be the Request's models + // 1. The model exists in the store, so set it to be the Request's models + request.identityModel = identityModel + } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { + // 2. The model exists in the user executor request.identityModel = identityModel } else if !request.prepareForExecution() { - // The models do not exist AND this request cannot be sent, drop this Request + // 3. The models do not exist AND this request cannot be sent, drop this Request addRequestQueue.remove(at: index) } } @@ -75,10 +78,13 @@ class OSIdentityOperationExecutor: OSOperationExecutor { // Hook each uncached Request to the model in the store for (index, request) in removeRequestQueue.enumerated().reversed() { if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // The model exists in the store, set it to be the Request's model + // 1. The model exists in the store, so set it to be the Request's model + request.identityModel = identityModel + } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { + // 2. The model exists in the user executor request.identityModel = identityModel } else if !request.prepareForExecution() { - // The model does not exist AND this request cannot be sent, drop this Request + // 3. The model does not exist AND this request cannot be sent, drop this Request removeRequestQueue.remove(at: index) } } @@ -158,47 +164,59 @@ class OSIdentityOperationExecutor: OSOperationExecutor { } func executeAddAliasesRequest(_ request: OSRequestAddAliases) { + guard !request.sentToClient else { + return + } + guard request.prepareForExecution() else { + return + } + request.sentToClient = true + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSIdentityOperationExecutor: executeAddAliasesRequest making request: \(request)") OneSignalClient.shared().execute(request) { _ in - // Mock a response - // TODO: Is there even a response to hydrate? - let response = ["onesignalId": UUID().uuidString, "label01": "id01"] - - // On success, remove request from cache, and hydrate model - // For example, if app restarts and we read in operations between sending this off and getting the response + // No hydration from response + // On success, remove request from cache self.addRequestQueue.removeAll(where: { $0 == request}) OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) - - // instead: modelstore.hydratewithresponse with modelid passed in.. request.modeltoupdate.modelId - // store can determine if modelid is same, then hydrate or do nothign - request.identityModel.hydrate(response) - } onFailure: { error in - self.addRequestQueue.removeAll(where: { $0 == request}) - OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription) + // 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) + } } } func executeRemoveAliasRequest(_ request: OSRequestRemoveAlias) { + guard !request.sentToClient else { + return + } + guard request.prepareForExecution() else { + return + } + request.sentToClient = true + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSIdentityOperationExecutor: executeRemoveAliasRequest making request: \(request)") OneSignalClient.shared().execute(request) { _ in - - // Mock a response - // TODO: Is there even a response to hydrate? - let response = ["onesignalId": UUID().uuidString, "label01": "id01"] - - // On success, remove request from cache, and hydrate model - // For example, if app restarts and we read in operations between sending this off and getting the response + // There is nothing to hydrate + // On success, remove request from cache self.removeRequestQueue.removeAll(where: { $0 == request}) OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) - - request.identityModel.hydrate(response) - } onFailure: { error in - self.removeRequestQueue.removeAll(where: { $0 == request}) - OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription) + 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) + } } } } From 8997fa37ad4bcec02d71068bd0ace92105f659e4 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 01:08:19 -0800 Subject: [PATCH 10/27] Update Subscription Executor * Hook up models from cache to requests when app starting up * Removing requests after success or unretryable failure * To aid, the Executor maintains dict of subscription models it has seen, just like the user executor --- .../OSSubscriptionOperationExecutor.swift | 97 ++++++++++++++----- 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift index bc9d43c20..4bf7b18c0 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift @@ -35,6 +35,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { var addRequestQueue: [OSRequestCreateSubscription] = [] var removeRequestQueue: [OSRequestDeleteSubscription] = [] var updateRequestQueue: [OSRequestUpdateSubscription] = [] + var subscriptionModels: [String: OSSubscriptionModel] = [:] init() { // Read unfinished deltas from cache, if any... @@ -56,20 +57,36 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { // Read unfinished requests from cache, if any... - if var addRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestCreateSubscription] { + var requestQueue: [OSRequestCreateSubscription] = [] + + if var cachedAddRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestCreateSubscription] { // Hook each uncached Request to the model in the store - for (index, request) in addRequestQueue.enumerated().reversed() { - if let subscriptionModel = getSubscriptionModelFromStores(modelId: request.subscriptionModel.modelId), - let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // The models exist in the stores, set it to be the Request's models + for request in cachedAddRequestQueue { + // 1. Hook up the subscription model + if let subscriptionModel = getSubscriptionModelFromStores(modelId: request.subscriptionModel.modelId) { + // a. The model exist in the store, set it to be the Request's models request.subscriptionModel = subscriptionModel + } else if let subscriptionModel = subscriptionModels[request.subscriptionModel.modelId] { + // b. The model exists in the dictionary of seen models + request.subscriptionModel = subscriptionModel + } else { + // c. The model has not been seen yet, add to dict + subscriptionModels[request.subscriptionModel.modelId] = request.subscriptionModel + } + // 2. Hook up the identity model + if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { + // a. The model exist in the store + request.identityModel = identityModel + } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { + // b. The model exist in the user executor request.identityModel = identityModel } else if !request.prepareForExecution() { - // The models do not exist AND this request cannot be sent, drop this Request - addRequestQueue.remove(at: index) + // The model do not exist AND this request cannot be sent, drop this Request + continue } + requestQueue.append(request) } - self.addRequestQueue = addRequestQueue + self.addRequestQueue = requestQueue } else { OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor error encountered reading from cache for \(OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY)") } @@ -78,10 +95,13 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { // Hook each uncached Request to the model in the store for (index, request) in removeRequestQueue.enumerated().reversed() { if let subscriptionModel = getSubscriptionModelFromStores(modelId: request.subscriptionModel.modelId) { - // The model exists in the store, set it to be the Request's model + // 1. The model exists in the store, set it to be the Request's model + request.subscriptionModel = subscriptionModel + } else if let subscriptionModel = subscriptionModels[request.subscriptionModel.modelId] { + // 2. The model exists in the dict of seen subscription models request.subscriptionModel = subscriptionModel } else if !request.prepareForExecution() { - // The model does not exist AND this request cannot be sent, drop this Request + // 3. The model does not exist AND this request cannot be sent, drop this Request removeRequestQueue.remove(at: index) } } @@ -94,10 +114,13 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { // Hook each uncached Request to the model in the store for (index, request) in updateRequestQueue.enumerated().reversed() { if let subscriptionModel = getSubscriptionModelFromStores(modelId: request.subscriptionModel.modelId) { - // The model exists in the store, set it to be the Request's model + // 1. The model exists in the store, set it to be the Request's model + request.subscriptionModel = subscriptionModel + } else if let subscriptionModel = subscriptionModels[request.subscriptionModel.modelId] { + // 2. The model exists in the dict of seen subscription models request.subscriptionModel = subscriptionModel } else if !request.prepareForExecution() { - // The models do not exist AND this request cannot be sent, drop this Request + // 3. The models do not exist AND this request cannot be sent, drop this Request updateRequestQueue.remove(at: index) } } @@ -202,13 +225,17 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { } func executeCreateSubscriptionRequest(_ request: OSRequestCreateSubscription) { + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { return } + request.sentToClient = true + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSSubscriptionOperationExecutor: executeCreateSubscriptionRequest making request: \(request)") OneSignalClient.shared().execute(request) { result in // On success, remove request from cache (even if not hydrating model), and hydrate model - // For example, if app restarts and we read in operations between sending this off and getting the response self.addRequestQueue.removeAll(where: { $0 == request}) OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) @@ -217,17 +244,26 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { return } request.subscriptionModel.hydrate(response) - } onFailure: { error in - self.addRequestQueue.removeAll(where: { $0 == request}) - OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription) + 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) + } } } func executeDeleteSubscriptionRequest(_ request: OSRequestDeleteSubscription) { + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { return } + request.sentToClient = true // This request can be executed as-is. OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSSubscriptionOperationExecutor: executeDeleteSubscriptionRequest making request: \(request)") @@ -239,17 +275,28 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) } onFailure: { error in - self.removeRequestQueue.removeAll(where: { $0 == request}) - OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription) + 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) + } } } func executeUpdateSubscriptionRequest(_ request: OSRequestUpdateSubscription) { - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSSubscriptionOperationExecutor: executeUpdateSubscriptionRequest making request: \(request)") - + guard !request.sentToClient else { + return + } guard request.prepareForExecution() else { return } + request.sentToClient = true + + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSSubscriptionOperationExecutor: executeUpdateSubscriptionRequest making request: \(request)") + OneSignalClient.shared().execute(request) { _ in // On success, remove request from cache. No model hydration occurs. @@ -258,8 +305,14 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) } onFailure: { error in - self.updateRequestQueue.removeAll(where: { $0 == request}) - OneSignalLog.onesignalLog(.LL_ERROR, message: error.debugDescription) + 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) + } } } } From f26508a992b2bcc5a23034aa1b6e6d246cbd1f25 Mon Sep 17 00:00:00 2001 From: Nan Date: Wed, 1 Feb 2023 17:17:50 -0800 Subject: [PATCH 11/27] Update Dev App * Including storyboard * Add/change methods --- .../OneSignalDevApp/AppDelegate.m | 5 - .../Base.lproj/Main.storyboard | 427 +++++++++++------- .../OneSignalDevApp/ViewController.h | 25 +- .../OneSignalDevApp/ViewController.m | 73 ++- .../Base.lproj/Main.storyboard | 48 +- .../OneSignalDevAppClip/ViewController.h | 10 +- .../OneSignalDevAppClip/ViewController.m | 20 +- 7 files changed, 391 insertions(+), 217 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m index b6821b64b..25ff718fb 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m @@ -84,11 +84,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [OneSignal setLaunchURLsInApp:YES]; [OneSignal setProvidesNotificationSettingsView:NO]; - - [OneSignal.Notifications requestPermission:^(BOOL accepted) { - NSLog(@"OneSignal Demo App requestPermission: %d", accepted); - }]; - [OneSignal.InAppMessages setLifecycleHandler:self]; [OneSignal.InAppMessages paused:true]; diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard index 1e045c93f..58bbbda8a 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard @@ -17,22 +17,22 @@ - + - + - + - - + @@ -186,93 +186,56 @@ - - - - - + + + + + - - + + - - - - + - + - + - - - - - - - - - - - - + + - - - - + - - - - + + + + - - - + + + + - - - + + - + - + + - - - + + + + - + - + + - + - + + - - + + + - + + + + + - + + - + + + - + + - - - + + + - + - - - + - - - + + + + - + - - - + - + + + - + - - + + + + + - @@ -578,9 +684,14 @@ - + + + + + + @@ -588,25 +699,27 @@ - + - + - + + - + + + + - - - + diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h index 107d43226..b80110dc7 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h @@ -38,17 +38,29 @@ @property (weak, nonatomic) IBOutlet UITextField *appIdTextField; @property (weak, nonatomic) IBOutlet UIButton *updateAppIdButton; @property (weak, nonatomic) IBOutlet UIButton *sendTagButton; -@property (weak, nonatomic) IBOutlet UIButton *getTagsButton; +@property (weak, nonatomic) IBOutlet UIButton *getInfoButton; @property (weak, nonatomic) IBOutlet UIButton *sendTagsButton; @property (weak, nonatomic) IBOutlet UIButton *promptPushButton; @property (weak, nonatomic) IBOutlet UIButton *promptLocationButton; @property (weak, nonatomic) IBOutlet UISegmentedControl *subscriptionSegmentedControl; @property (weak, nonatomic) IBOutlet UITextField *emailTextField; -@property (weak, nonatomic) IBOutlet UIButton *setEmailButton; -@property (weak, nonatomic) IBOutlet UIButton *logoutEmailButton; +@property (weak, nonatomic) IBOutlet UIButton *addEmailButton; +@property (weak, nonatomic) IBOutlet UIButton *removeEmailButton; + +@property (weak, nonatomic) IBOutlet UITextField *smsTextField; +@property (weak, nonatomic) IBOutlet UIButton *addSmsButton; +@property (weak, nonatomic) IBOutlet UIButton *removeSmsButton; + @property (weak, nonatomic) IBOutlet UITextField *externalUserIdTextField; -@property (weak, nonatomic) IBOutlet UIButton *setExternalUserIdButton; -@property (weak, nonatomic) IBOutlet UIButton *removeExternalUserIdButton; +@property (weak, nonatomic) IBOutlet UIButton *loginExternalUserIdButton; +@property (weak, nonatomic) IBOutlet UIButton *logoutButton; + +@property (weak, nonatomic) IBOutlet UITextField *addAliasLabelTextField; +@property (weak, nonatomic) IBOutlet UITextField *addAliasIdTextField; +@property (weak, nonatomic) IBOutlet UIButton *addAliasButton; +@property (weak, nonatomic) IBOutlet UITextField *removeAliasLabelTextField; +@property (weak, nonatomic) IBOutlet UIButton *removeAliasButton; + @property (weak, nonatomic) IBOutlet UISegmentedControl *locationSharedSegementedControl; @property (weak, nonatomic) IBOutlet UISegmentedControl *inAppMessagingSegmentedControl; @property (weak, nonatomic) IBOutlet UITextField *addTriggerKey; @@ -61,10 +73,11 @@ @property (weak, nonatomic) IBOutlet UITextField *outcomeValueName; @property (weak, nonatomic) IBOutlet UITextField *outcomeValue; @property (weak, nonatomic) IBOutlet UITextField *outcomeUniqueName; -@property (weak, nonatomic) IBOutlet UITextView *result; @property (weak, nonatomic) IBOutlet UITextField *tagKey; @property (weak, nonatomic) IBOutlet UITextField *tagValue; @property (weak, nonatomic) IBOutlet UITextField *activityId; +@property (weak, nonatomic) IBOutlet UITextField *languageTextField; + @end diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index a134e2cfd..c474a4f59 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -42,7 +42,7 @@ - (void)viewDidLoad { self.consentSegmentedControl.selectedSegmentIndex = (NSInteger) ![OneSignal requiresPrivacyConsent]; -// self.subscriptionSegmentedControl.selectedSegmentIndex = (NSInteger) OneSignal.getDeviceState.isSubscribed; + self.subscriptionSegmentedControl.selectedSegmentIndex = (NSInteger) OneSignal.User.pushSubscription.optedIn; self.locationSharedSegementedControl.selectedSegmentIndex = (NSInteger) [OneSignal.Location isShared]; @@ -60,7 +60,8 @@ - (void)changeAnimationState:(BOOL)animating { } - (IBAction)updateAppId:(id)sender { - [AppDelegate setOneSignalAppId:self.appIdTextField.text]; + // [AppDelegate setOneSignalAppId:self.appIdTextField.text]; + NSLog(@"Dev App: Not a feature, can't change app id, no op!"); } - (IBAction)addTriggerAction:(id)sender { @@ -84,16 +85,41 @@ - (IBAction)getTriggersAction:(id)sender { NSLog(@"Getting triggers no longer supported"); } -- (IBAction)setEmailButton:(id)sender { +- (IBAction)addEmailButton:(id)sender { NSString *email = self.emailTextField.text; - NSLog(@"Adding email with email: %@", email); + NSLog(@"Dev App: add email: %@", email); [OneSignal.User addEmail:email]; } -- (IBAction)logoutEmailButton:(id)sender { +- (IBAction)removeEmailButton:(id)sender { NSString *email = self.emailTextField.text; - BOOL canRemove = [OneSignal.User removeEmail:email]; - NSLog(@"Removing email with email: %@ and canRemove: %d", email, canRemove); + NSLog(@"Dev App: Removing email: %@", email); + [OneSignal.User removeEmail:email]; +} + +- (IBAction)addSmsButton:(id)sender { + NSString *sms = self.smsTextField.text; + NSLog(@"Dev App: Add sms: %@", sms); + [OneSignal.User addSmsNumber:sms]; +} + +- (IBAction)removeSmsButton:(id)sender { + NSString *sms = self.smsTextField.text; + NSLog(@"Dev App: Removing sms: %@", sms); + [OneSignal.User removeSmsNumber:sms]; +} + +- (IBAction)addAliasButton:(UIButton *)sender { + NSString* label = self.addAliasLabelTextField.text; + NSString* id = self.addAliasIdTextField.text; + NSLog(@"Dev App: Add alias with label %@ and ID %@", label, id); + [OneSignal.User addAliasWithLabel:label id:id]; +} + +- (IBAction)removeAliasButton:(UIButton *)sender { + NSString* label = self.removeAliasLabelTextField.text; + NSLog(@"Dev App: Removing alias with label %@", label); + [OneSignal.User removeAlias:label]; } - (IBAction)sendTagButton:(id)sender { @@ -104,8 +130,14 @@ - (IBAction)sendTagButton:(id)sender { } } -- (IBAction)getTagsButton:(id)sender { - NSLog(@"getTags no longer supported"); +- (IBAction)getInfoButton:(id)sender { + NSLog(@"💛 Dev App: get User and Device information"); + [OneSignalUserManagerImpl.sharedInstance internalDumpInfo]; + NSLog(@"💛 Dev App: OneSignal.Notifications permission: %d", [OneSignal.Notifications permission]); + NSLog(@"💛 Dev App: OneSignal.Notifications.canRequestPermission: %d", [OneSignal.Notifications canRequestPermission]); + [OneSignal internalDumpInfo]; + NSLog(@"💛 Dev App: getPrivacyConsent: %d", OneSignal.getPrivacyConsent); + NSLog(@"💛 Dev App: requiresPrivacyConsent: %d", [OneSignal requiresPrivacyConsent]); } - (IBAction)sendTagsButton:(id)sender { @@ -147,7 +179,13 @@ - (IBAction)consentSegmentedControlValueChanged:(UISegmentedControl *)sender { - (IBAction)subscriptionSegmentedControlValueChanged:(UISegmentedControl *)sender { NSLog(@"View controller subscription status: %i", (int) sender.selectedSegmentIndex); - // [OneSignal disablePush:(bool) !sender.selectedSegmentIndex]; + if (sender.selectedSegmentIndex) { + [OneSignal.User.pushSubscription optIn]; + } else { + [OneSignal.User.pushSubscription optOut]; + } + sender.selectedSegmentIndex = (NSInteger) OneSignal.User.pushSubscription.optedIn; + } - (IBAction)locationSharedSegmentedControlValueChanged:(UISegmentedControl *)sender { @@ -164,13 +202,13 @@ - (void)handleMessageAction:(NSString *)actionId { NSLog(@"View controller did get action: %@", actionId); } -- (IBAction)setExternalUserId:(UIButton *)sender { +- (IBAction)loginExternalUserId:(UIButton *)sender { NSString* externalUserId = self.externalUserIdTextField.text; NSLog(@"Dev App: Logging in to external user ID %@", externalUserId); [OneSignal login:externalUserId]; } -- (IBAction)removeExternalUserId:(UIButton *)sender { +- (IBAction)logout:(UIButton *)sender { NSLog(@"Dev App: Logout called."); [OneSignal logout]; } @@ -223,4 +261,15 @@ - (IBAction)exitLiveActivity:(id)sender { } } +- (IBAction)setLanguage:(id)sender { + NSLog(@"Dev App: set language called."); + NSString *language = self.languageTextField.text; + [OneSignal.User setLanguage:language]; +} + +- (IBAction)clearAllNotifications:(id)sender { + NSLog(@"Dev App: clear All Notifications called."); + [OneSignal.Notifications clearAll]; +} + @end diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/Base.lproj/Main.storyboard index 23dc59463..ac9223f99 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/Base.lproj/Main.storyboard +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/Base.lproj/Main.storyboard @@ -1,7 +1,9 @@ - + + - + + @@ -18,7 +20,7 @@ - + @@ -38,9 +40,9 @@ - + - + @@ -303,12 +305,12 @@ @@ -328,9 +330,9 @@ - + - + diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h index f2533a5ae..956c2cacb 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h @@ -37,17 +37,17 @@ @property (weak, nonatomic) IBOutlet UISegmentedControl *consentSegmentedControl; @property (weak, nonatomic) IBOutlet UITextField *appIdTextField; @property (weak, nonatomic) IBOutlet UIButton *updateAppIdButton; -@property (weak, nonatomic) IBOutlet UIButton *getTagsButton; +@property (weak, nonatomic) IBOutlet UIButton *getInfoButton; @property (weak, nonatomic) IBOutlet UIButton *sendTagsButton; @property (weak, nonatomic) IBOutlet UIButton *promptPushButton; @property (weak, nonatomic) IBOutlet UIButton *promptLocationButton; @property (weak, nonatomic) IBOutlet UISegmentedControl *subscriptionSegmentedControl; @property (weak, nonatomic) IBOutlet UITextField *emailTextField; -@property (weak, nonatomic) IBOutlet UIButton *setEmailButton; -@property (weak, nonatomic) IBOutlet UIButton *logoutEmailButton; +@property (weak, nonatomic) IBOutlet UIButton *addEmailButton; +@property (weak, nonatomic) IBOutlet UIButton *removeEmailButton; @property (weak, nonatomic) IBOutlet UITextField *externalUserIdTextField; -@property (weak, nonatomic) IBOutlet UIButton *setExternalUserIdButton; -@property (weak, nonatomic) IBOutlet UIButton *removeExternalUserIdButton; +@property (weak, nonatomic) IBOutlet UIButton *loginExternalUserIdButton; +@property (weak, nonatomic) IBOutlet UIButton *logoutButton; @property (weak, nonatomic) IBOutlet UISegmentedControl *locationSharedSegementedControl; @property (weak, nonatomic) IBOutlet UISegmentedControl *inAppMessagingSegmentedControl; @property (weak, nonatomic) IBOutlet UITextField *addTriggerKey; diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.m index 48b9ee8f6..d1ada47a2 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.m @@ -78,19 +78,19 @@ - (IBAction)getTriggersAction:(id)sender { NSLog(@"Getting triggers no longer supported"); } -- (IBAction)setEmailButton:(id)sender { +- (IBAction)addEmailButton:(id)sender { NSString *email = self.emailTextField.text; - NSLog(@"Adding email with email: %@", email); + NSLog(@"Dev App Clip: Adding email: %@", email); [OneSignal.User addEmail:email]; } -- (IBAction)logoutEmailButton:(id)sender { +- (IBAction)removeEmailButton:(id)sender { NSString *email = self.emailTextField.text; - BOOL canRemove = [OneSignal.User removeEmail:email]; - NSLog(@"Removing email with email: %@ and canRemove: %d", email, canRemove); + NSLog(@"Dev App Clip: Removing email: %@", email); + [OneSignal.User removeEmail:email]; } -- (IBAction)getTagsButton:(id)sender { +- (IBAction)getInfoButton:(id)sender { NSLog(@"getTags no longer supported"); } @@ -150,12 +150,14 @@ - (void)handleMessageAction:(NSString *)actionId { NSLog(@"View controller did get action: %@", actionId); } -- (IBAction)setExternalUserId:(UIButton *)sender { +- (IBAction)loginExternalUserId:(UIButton *)sender { NSLog(@"setExternalUserId is no longer supported. Please use login or addAlias."); + // TODO: Update } -- (IBAction)removeExternalUserId:(UIButton *)sender { - NSLog(@"setExternalUserId is no longer supported. Please use logout or removeAlias."); +- (IBAction)logout:(UIButton *)sender { + NSLog(@"removeExternalUserId is no longer supported. Please use logout or removeAlias."); + // TODO: Update } #pragma mark UITextFieldDelegate Methods From 6bbdca6e3d2ab3fd9669e9849709485b1e3476f1 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 12:29:08 -0800 Subject: [PATCH 12/27] [nits] from swiflint * Not related to user model, but Committing these so it doesn't show up every time swiftlint is run --- .../OneSignalDevApp/LiveActivityController.swift | 4 ++-- .../OneSignalWidgetExtension/OneSignalWidgetExtension.swift | 6 +++--- .../OneSignalWidgetExtensionLiveActivity.swift | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/LiveActivityController.swift b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/LiveActivityController.swift index afd976ce7..3aeb29f19 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/LiveActivityController.swift +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/LiveActivityController.swift @@ -46,7 +46,7 @@ class LiveActivityController: NSObject { @objc static func createActivity() async -> String? { if #available(iOS 16.1, *) { - counter += 1; + counter += 1 let attributes = OneSignalWidgetAttributes(title: "#" + String(counter) + " OneSignal Dev App Live Activity") let contentState = OneSignalWidgetAttributes.ContentState(message: "Update this message through push or with Activity Kit") do { @@ -58,7 +58,7 @@ class LiveActivityController: NSObject { let myToken = data.map {String(format: "%02x", $0)}.joined() return myToken } - } catch (let error) { + } catch let error { print(error.localizedDescription) return nil } diff --git a/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtension.swift b/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtension.swift index afa1ce6b5..5da0476d0 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtension.swift +++ b/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtension.swift @@ -34,12 +34,12 @@ struct Provider: IntentTimelineProvider { SimpleEntry(date: Date(), configuration: ConfigurationIntent()) } - func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { + func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> Void) { let entry = SimpleEntry(date: Date(), configuration: configuration) completion(entry) } - func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) { + func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> Void) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. @@ -60,7 +60,7 @@ struct SimpleEntry: TimelineEntry { let configuration: ConfigurationIntent } -struct OneSignalWidgetExtensionEntryView : View { +struct OneSignalWidgetExtensionEntryView: View { var entry: Provider.Entry var body: some View { diff --git a/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtensionLiveActivity.swift b/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtensionLiveActivity.swift index e99eb171b..6df671885 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtensionLiveActivity.swift +++ b/iOS_SDK/OneSignalDevApp/OneSignalWidgetExtension/OneSignalWidgetExtensionLiveActivity.swift @@ -29,7 +29,6 @@ import ActivityKit import WidgetKit import SwiftUI - struct OneSignalWidgetExtensionLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: OneSignalWidgetAttributes.self) { context in @@ -54,7 +53,7 @@ struct OneSignalWidgetExtensionLiveActivity: Widget { } .activitySystemActionForegroundColor(.black) .activityBackgroundTint(.white) - } dynamicIsland: { context in + } dynamicIsland: { _ in DynamicIsland { // Expanded UI goes here. Compose the expanded UI through // various regions, like leading/trailing/center/bottom From 9470f077fd67f34f5938f48bf0268defd76fe994 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 16:24:16 -0800 Subject: [PATCH 13/27] [dev app] add buttons for requiring privacy consent --- .../Base.lproj/Main.storyboard | 28 +++++++++++++++++-- .../OneSignalDevApp/ViewController.m | 10 +++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard index 58bbbda8a..14cbdab24 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard @@ -501,15 +501,33 @@ - + + @@ -550,6 +568,7 @@ + @@ -567,6 +586,7 @@ + @@ -613,8 +633,10 @@ + + @@ -639,10 +661,12 @@ + + diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index c474a4f59..4792be20e 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -272,4 +272,14 @@ - (IBAction)clearAllNotifications:(id)sender { [OneSignal.Notifications clearAll]; } +- (IBAction)requireConsent:(id)sender { + NSLog(@"Dev App: setting setRequiresPrivacyConsent to true."); + [OneSignal setRequiresPrivacyConsent:true]; +} + +- (IBAction)dontRequireConsent:(id)sender { + NSLog(@"Dev App: setting setRequiresPrivacyConsent to false."); + [OneSignal setRequiresPrivacyConsent:false]; +} + @end From 1d8e1e7aa73015a8440429c249833db3e1521c27 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 2 Feb 2023 22:48:33 -0800 Subject: [PATCH 14/27] update email and sms subscriptions API * addSms becomes addSms * removeSmsNumber becomes removeSms * return void when removing, not bool --- .../OneSignalDevApp/ViewController.m | 4 ++-- .../OneSignalOSCore/Source/OSModelStore.swift | 4 +--- .../Source/OneSignalUserManagerImpl.swift | 20 +++++++++---------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index 4792be20e..f3a571201 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -100,13 +100,13 @@ - (IBAction)removeEmailButton:(id)sender { - (IBAction)addSmsButton:(id)sender { NSString *sms = self.smsTextField.text; NSLog(@"Dev App: Add sms: %@", sms); - [OneSignal.User addSmsNumber:sms]; + [OneSignal.User addSms:sms]; } - (IBAction)removeSmsButton:(id)sender { NSString *sms = self.smsTextField.text; NSLog(@"Dev App: Removing sms: %@", sms); - [OneSignal.User removeSmsNumber:sms]; + [OneSignal.User removeSms:sms]; } - (IBAction)addAliasButton:(UIButton *)sender { diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift index edfc9203f..2a4bf6e20 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift @@ -112,7 +112,7 @@ open class OSModelStore: NSObject { Returns false if this model does not exist in the store. This can happen if remove email or SMS is called and it doesn't exist in the store. */ - public func remove(_ id: String) -> Bool { + public func remove(_ id: String) { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSModelStore remove() called with model \(id)") // TODO: Nothing will happen if model doesn't exist in the store if let model = models[id] { @@ -127,9 +127,7 @@ open class OSModelStore: NSObject { self.changeSubscription.fire { modelStoreListener in modelStoreListener.onRemoved(model) } - return true } - return false } /** diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index d0066b113..9cf3d80a7 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -60,10 +60,10 @@ import OneSignalNotifications func removeTags(_ tags: [String]) // Email func addEmail(_ email: String) - func removeEmail(_ email: String) -> Bool + func removeEmail(_ email: String) // SMS - func addSmsNumber(_ number: String) - func removeSmsNumber(_ number: String) -> Bool + func addSms(_ number: String) + func removeSms(_ number: String) // Language func setLanguage(_ language: String) // JWT Token Expire @@ -612,16 +612,16 @@ extension OneSignalUserManagerImpl: OSUser { This will be a no-op and no request will be made. Error handling needs to be implemented in the future. */ - public func removeEmail(_ email: String) -> Bool { + public func removeEmail(_ email: String) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "removeEmail") else { - return false + return } // Check if is valid email? createUserIfNil() - return self.subscriptionModelStore.remove(email) + self.subscriptionModelStore.remove(email) } - public func addSmsNumber(_ number: String) { + public func addSms(_ number: String) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "addSmsNumber") else { return } @@ -644,13 +644,13 @@ extension OneSignalUserManagerImpl: OSUser { This will be a no-op and no request will be made. Error handling needs to be implemented in the future. */ - public func removeSmsNumber(_ number: String) -> Bool { + public func removeSms(_ number: String) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "removeSmsNumber") else { - return false + return } // Check if is valid SMS? createUserIfNil() - return self.subscriptionModelStore.remove(number) + self.subscriptionModelStore.remove(number) } public func setLanguage(_ language: String) { From 102cdf7a6a37cc22b69171980e31f2238605069a Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 00:23:11 -0800 Subject: [PATCH 15/27] update permission observer callback's object * No longer pass a `OSPermissionStateChanges` object with a `to` and `from` * Instead, we wanted to pass a single boolean to represent the current permission * However, the observable infrastructure set up does not allow us to pass bools * Let's for now pass a `OSPermissionState` object representing the current state with 1 property on it called `permission` * Updated SDK minimally to support this case, unused codes could be cleaned up... --- .../OneSignalDevApp/AppDelegate.m | 6 +- .../OneSignalDevAppClip/AppDelegate.m | 4 +- .../OSNotificationsManager.m | 5 +- .../OneSignalNotifications/OSPermission.h | 21 ++---- .../OneSignalNotifications/OSPermission.m | 67 +++---------------- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 13 +--- 6 files changed, 24 insertions(+), 92 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m index 25ff718fb..06db3b89d 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m @@ -117,12 +117,12 @@ + (void) setOneSignalAppId:(NSString*)onesignalAppId { // [OneSignal setAppId:onesignalAppId]; } -- (void) onOSPermissionChanged:(OSPermissionStateChanges*)stateChanges { - NSLog(@"onOSPermissionChanged: %@", stateChanges); +- (void)onOSPermissionChanged:(OSPermissionState*)state { + NSLog(@"Dev App onOSPermissionChanged: %@", state); } - (void)onOSPushSubscriptionChangedWithStateChanges:(OSPushSubscriptionStateChanges *)stateChanges { - NSLog(@"onOSPushSubscriptionChangedWithStateChanges: %@", stateChanges); + NSLog(@"Dev App onOSPushSubscriptionChangedWithStateChanges: %@", stateChanges); ViewController* mainController = (ViewController*) self.window.rootViewController; mainController.subscriptionSegmentedControl.selectedSegmentIndex = (NSInteger) stateChanges.to.optedIn; } diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m index 59e6ffb7d..97c051370 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m @@ -112,8 +112,8 @@ + (void) setOneSignalAppId:(NSString*)onesignalAppId { [OneSignal initialize:onesignalAppId withLaunchOptions:nil]; } -- (void) onOSPermissionChanged:(OSPermissionStateChanges*)stateChanges { - NSLog(@"onOSPermissionChanged: %@", stateChanges); +- (void) onOSPermissionChanged:(OSPermissionState*)state { + NSLog(@"onOSPermissionChanged: %@", state); } // TODO: Add push sub observer diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m index e29b8d1c0..c1c3ad22e 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m @@ -143,6 +143,7 @@ + (OneSignalNotificationSettings *)osNotificationSettings { return _osNotificationSettings; } +// static property def to add developer's OSPermissionStateChanges observers to. static ObservablePermissionStateChangesType* _permissionStateChangesObserver; + (ObservablePermissionStateChangesType*)permissionStateChangesObserver { if (!_permissionStateChangesObserver) @@ -421,11 +422,11 @@ + (void)setSubscriptionErrorStatus:(int)errorType { [self sendNotificationTypesUpdateToDelegate]; } -// onOSPermissionChanged should only fire if something changed. +// onOSPermissionChanged should only fire if the reachable property changed. + (void)addPermissionObserver:(NSObject*)observer { [self.permissionStateChangesObserver addObserver:observer]; - if ([self.currentPermissionState compare:self.lastPermissionState]) + if (self.currentPermissionState.reachable != self.lastPermissionState.reachable) [OSPermissionChangedInternalObserver fireChangesObserver:self.currentPermissionState]; } diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.h b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.h index aa6e90020..54ef6a728 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.h +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.h @@ -49,13 +49,9 @@ typedef NS_ENUM(NSInteger, OSNotificationPermission) { // Permission Classes @interface OSPermissionState : NSObject // TODO: Decide: remove/change properties after addition of canRequestPermission and permission boolean -@property (readonly, nonatomic) BOOL reachable; -@property (readonly, nonatomic) BOOL hasPrompted; -@property (readonly, nonatomic) BOOL provisional; -@property (readonly, nonatomic) BOOL providesAppNotificationSettings; -@property (readonly, nonatomic) OSNotificationPermission status; +@property (readonly, nonatomic) BOOL permission; - (NSDictionary * _Nonnull)jsonRepresentation; -- (instancetype _Nonnull )initWithStatus:(OSNotificationPermission)status reachable:(BOOL)reachable hasPrompted:(BOOL)hasPrompted provisional:(BOOL)provisional providesAppNotificationSettings:(BOOL)providesAppNotificationSettings; +- (instancetype _Nonnull )initWithPermission:(BOOL)permission; @end @protocol OSPermissionStateObserver @@ -87,24 +83,15 @@ typedef OSObservable*, OSPermissionState*> O - (instancetype _Nonnull )initAsTo; - (instancetype _Nonnull )initAsFrom; -- (BOOL)compare:(OSPermissionStateInternal * _Nonnull)from; - (OSPermissionState * _Nonnull)getExternalState; - -@end - -@interface OSPermissionStateChanges : NSObject - -@property (readonly, nonnull) OSPermissionState* to; -@property (readonly, nonnull) OSPermissionState* from; - (NSDictionary * _Nonnull)jsonRepresentation; -- (instancetype _Nonnull)initAsTo:(OSPermissionState * _Nonnull)to from:(OSPermissionState * _Nonnull)from; @end @protocol OSPermissionObserver -- (void)onOSPermissionChanged:(OSPermissionStateChanges * _Nonnull)stateChanges; +- (void)onOSPermissionChanged:(OSPermissionState * _Nonnull)state; @end -typedef OSObservable*, OSPermissionStateChanges*> ObservablePermissionStateChangesType; +typedef OSObservable*, OSPermissionState*> ObservablePermissionStateChangesType; @interface OSPermissionChangedInternalObserver : NSObject diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m index f6dd120a0..cdcec3602 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m @@ -159,15 +159,8 @@ - (OSNotificationPermission)status { return OSNotificationPermissionNotDetermined; } -- (BOOL)compare:(OSPermissionStateInternal*)from { - return self.accepted != from.accepted || - self.ephemeral != from.ephemeral || - self.answeredPrompt != from.answeredPrompt || - self.hasPrompted != from.hasPrompted; -} - - (OSPermissionState *)getExternalState { - return [[OSPermissionState alloc] initWithStatus:self.status reachable:self.reachable hasPrompted:self.hasPrompted provisional:self.provisional providesAppNotificationSettings:self.providesAppNotificationSettings]; + return [[OSPermissionState alloc] initWithPermission:self.reachable]; } - (NSString*)description { @@ -181,40 +174,18 @@ - (NSString*)description { @implementation OSPermissionState -- (instancetype)initWithStatus:(OSNotificationPermission)status reachable:(BOOL)reachable hasPrompted:(BOOL)hasPrompted provisional:(BOOL)provisional providesAppNotificationSettings:(BOOL)providesAppNotificationSettings { - _status = status; - _reachable = reachable; - _hasPrompted = hasPrompted; - _providesAppNotificationSettings = providesAppNotificationSettings; - _provisional = provisional; +- (instancetype)initWithPermission:(BOOL)permission { + _permission = permission; return self; } -- (NSString*)statusAsString { - switch(self.status) { - case OSNotificationPermissionNotDetermined: - return @"NotDetermined"; - case OSNotificationPermissionAuthorized: - return @"Authorized"; - case OSNotificationPermissionDenied: - return @"Denied"; - case OSNotificationPermissionProvisional: - return @"Provisional"; - case OSNotificationPermissionEphemeral: - return @"Ephemeral"; - } - return @"NotDetermined"; -} - - (NSString*)description { - static NSString* format = @""; - return [NSString stringWithFormat:format, self.hasPrompted, self.statusAsString, self.provisional]; + static NSString* format = @""; + return [NSString stringWithFormat:format, self.permission]; } - (NSDictionary*)jsonRepresentation { - return @{@"hasPrompted": @(self.hasPrompted), - @"status": @(self.status), - @"provisional" : @(self.provisional)}; + return @{@"permission": @(self.permission)}; } @end @@ -227,9 +198,12 @@ - (void)onChanged:(OSPermissionStateInternal*)state { + (void)fireChangesObserver:(OSPermissionStateInternal*)state { OSPermissionState *externalToState = [state getExternalState]; OSPermissionState *externalFromState = [OSNotificationsManager.lastPermissionState getExternalState]; - OSPermissionStateChanges* stateChanges = [[OSPermissionStateChanges alloc] initAsTo:externalToState from:externalFromState]; - BOOL hasReceiver = [OSNotificationsManager.permissionStateChangesObserver notifyChange:stateChanges]; + if (externalToState.permission == externalFromState.permission) { + return; + } + + BOOL hasReceiver = [OSNotificationsManager.permissionStateChangesObserver notifyChange:externalToState]; if (hasReceiver) { OSNotificationsManager.lastPermissionState = [state copy]; [OSNotificationsManager.lastPermissionState persistAsFrom]; @@ -242,22 +216,3 @@ + (void)fireChangesObserver:(OSPermissionStateInternal*)state { } @end - -@implementation OSPermissionStateChanges - -- (NSString*)description { - static NSString* format = @""; - return [NSString stringWithFormat:format, _from, _to]; -} - -- (NSDictionary*)jsonRepresentation { - return @{@"from": [_from jsonRepresentation], @"to": [_to jsonRepresentation]}; -} - -- (instancetype)initAsTo:(OSPermissionState *)to from:(OSPermissionState *)from { - _to = to; - _from = from; - return self; -} - -@end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index cdeb5a45a..2414b935e 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -121,15 +121,6 @@ + (DelayedConsentInitializationParameters *)delayedInitParameters { // Set when the app is launched static NSDate *sessionLaunchTime; -// static property def to add developer's OSPermissionStateChanges observers to. -static ObservablePermissionStateChangesType* _permissionStateChangesObserver; -+ (ObservablePermissionStateChangesType*)permissionStateChangesObserver { - if (!_permissionStateChangesObserver) - _permissionStateChangesObserver = [[OSObservable alloc] initWithChangeSelector:@selector(onOSPermissionChanged:)]; - return _permissionStateChangesObserver; -} - - /* Indicates if the iOS params request has started Set to true when the method is called and set false if the request's failure callback is triggered @@ -198,9 +189,7 @@ + (void)clearStatics { [OSNotificationsManager clearStatics]; registeredWithApple = false; - - _permissionStateChangesObserver = nil; - + _downloadedParameters = false; _didCallDownloadParameters = false; From 611e3d68fc912dde936047dee6f2c30265576b7c Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 01:09:33 -0800 Subject: [PATCH 16/27] rename push subscription model's `accepted` property to `reachable` * Reachable is a more accurate representation of this property. * Accepted implies explicit acceptance whereas this is really whether push notifications can be displayed and include provisional. --- .../OSNotificationsManager.h | 2 +- .../OSNotificationsManager.m | 4 +- .../OneSignalNotifications/OSPermission.m | 4 +- .../Source/OSSubscriptionModel.swift | 50 +++++++++---------- .../OneSignalUser/Source/OSUserRequests.swift | 2 +- .../Source/OneSignalUserManagerImpl.swift | 14 +++--- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.h b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.h index 4922e38bb..ec9fd549f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.h +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.h @@ -57,7 +57,7 @@ typedef void (^OSNotificationOpenedBlock)(OSNotificationOpenedResult * _Nonnull // can check responds to selector - (void)setNotificationTypes:(int)notificationTypes; - (void)setPushToken:(NSString * _Nonnull)pushToken; -- (void)setAccepted:(BOOL)inAccepted; +- (void)setReachable:(BOOL)inReachable; @end diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m index c1c3ad22e..fccb328a7 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m @@ -456,8 +456,8 @@ + (void)updateNotificationTypes:(int)notificationTypes { + (void)sendNotificationTypesUpdateToDelegate { // We don't delay observer update to wait until the OneSignal server is notified // TODO: We can do the above and delay observers until server is updated. - if (self.delegate && [self.delegate respondsToSelector:@selector(setAccepted:)]) { - [self.delegate setAccepted:[self getNotificationTypes] > 0]; + if (self.delegate && [self.delegate respondsToSelector:@selector(setReachable:)]) { + [self.delegate setReachable:[self getNotificationTypes] > 0]; } if (self.delegate && [self.delegate respondsToSelector:@selector(setNotificationTypes:)]) { [self.delegate setNotificationTypes:[self getNotificationTypes]]; diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m index cdcec3602..0ae54bf99 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSPermission.m @@ -210,8 +210,8 @@ + (void)fireChangesObserver:(OSPermissionStateInternal*)state { } // Update the push subscription's _accepted property // TODO: This can be called before the User Manager has set itself as the delegate - if (OSNotificationsManager.delegate && [OSNotificationsManager.delegate respondsToSelector:@selector(setAccepted:)]) { - [OSNotificationsManager.delegate setAccepted:state.reachable]; + if (OSNotificationsManager.delegate && [OSNotificationsManager.delegate respondsToSelector:@selector(setReachable:)]) { + [OSNotificationsManager.delegate setReachable:state.reachable]; } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModel.swift index cba955012..345d98e85 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModel.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModel.swift @@ -135,14 +135,14 @@ class OSSubscriptionModel: OSModel { // Internal property to send to server, not meant for outside access var enabled: Bool { // Does not consider subscription_id in the calculation get { - return calculateIsEnabled(address: address, accepted: _accepted, isDisabled: _isDisabled) + return calculateIsEnabled(address: address, reachable: _reachable, isDisabled: _isDisabled) } } var optedIn: Bool { // optedIn = permission + userPreference get { - return calculateIsOptedIn(accepted: _accepted, isDisabled: _isDisabled) + return calculateIsOptedIn(reachable: _reachable, isDisabled: _isDisabled) } } @@ -169,13 +169,13 @@ class OSSubscriptionModel: OSModel { Defaults to true for email & SMS, defaults to false for push. Note that this property reflects the `reachable` property of a permission state. As provisional permission is considered to be `optedIn` and `enabled`. */ - var _accepted: Bool { + var _reachable: Bool { didSet { - guard self.type == .push && _accepted != oldValue else { + guard self.type == .push && _reachable != oldValue else { return } updateNotificationTypes() - firePushSubscriptionChanged(.accepted(oldValue)) + firePushSubscriptionChanged(.reachable(oldValue)) } } @@ -202,13 +202,13 @@ class OSSubscriptionModel: OSModel { init(type: OSSubscriptionType, address: String?, subscriptionId: String?, - accepted: Bool, + reachable: Bool, isDisabled: Bool, changeNotifier: OSEventProducer) { self.type = type self.address = address self.subscriptionId = subscriptionId - _accepted = accepted + _reachable = reachable _isDisabled = isDisabled // Set test_type if subscription model is PUSH @@ -234,7 +234,7 @@ class OSSubscriptionModel: OSModel { coder.encode(type.rawValue, forKey: "type") // Encodes as String coder.encode(address, forKey: "address") coder.encode(subscriptionId, forKey: "subscriptionId") - coder.encode(_accepted, forKey: "_accepted") + coder.encode(_reachable, forKey: "_reachable") coder.encode(_isDisabled, forKey: "_isDisabled") coder.encode(notificationTypes, forKey: "notificationTypes") coder.encode(testType, forKey: "testType") @@ -251,7 +251,7 @@ class OSSubscriptionModel: OSModel { self.type = type self.address = coder.decodeObject(forKey: "address") as? String self.subscriptionId = coder.decodeObject(forKey: "subscriptionId") as? String - self._accepted = coder.decodeBool(forKey: "_accepted") + self._reachable = coder.decodeBool(forKey: "_reachable") self._isDisabled = coder.decodeBool(forKey: "_isDisabled") self.notificationTypes = coder.decodeInteger(forKey: "notificationTypes") self.testType = coder.decodeObject(forKey: "testType") as? Int @@ -300,14 +300,14 @@ extension OSSubscriptionModel { // Calculates if the device is opted in to push notification. // Must have permission and not be opted out. - func calculateIsOptedIn(accepted: Bool, isDisabled: Bool) -> Bool { - return accepted && !isDisabled + func calculateIsOptedIn(reachable: Bool, isDisabled: Bool) -> Bool { + return reachable && !isDisabled } // Calculates if push notifications are enabled on the device. // Does not consider the existence of the subscription_id, as we send this in the request to create a push subscription. - func calculateIsEnabled(address: String?, accepted: Bool, isDisabled: Bool) -> Bool { - return address != nil && accepted && !isDisabled + func calculateIsEnabled(address: String?, reachable: Bool, isDisabled: Bool) -> Bool { + return address != nil && reachable && !isDisabled } func updateNotificationTypes() { @@ -316,7 +316,7 @@ extension OSSubscriptionModel { enum OSPushPropertyChanged { case subscriptionId(String?) - case accepted(Bool) + case reachable(Bool) case isDisabled(Bool) case address(String?) } @@ -328,29 +328,29 @@ extension OSSubscriptionModel { switch changedProperty { case .subscriptionId(let oldValue): - prevIsEnabled = calculateIsEnabled(address: address, accepted: _accepted, isDisabled: _isDisabled) - prevIsOptedIn = calculateIsOptedIn(accepted: _accepted, isDisabled: _isDisabled) + prevIsEnabled = calculateIsEnabled(address: address, reachable: _reachable, isDisabled: _isDisabled) + prevIsOptedIn = calculateIsOptedIn(reachable: _reachable, isDisabled: _isDisabled) prevSubscriptionState = OSPushSubscriptionState(id: oldValue, token: address, optedIn: prevIsOptedIn) - case .accepted(let oldValue): - prevIsEnabled = calculateIsEnabled(address: address, accepted: oldValue, isDisabled: _isDisabled) - prevIsOptedIn = calculateIsOptedIn(accepted: oldValue, isDisabled: _isDisabled) + case .reachable(let oldValue): + prevIsEnabled = calculateIsEnabled(address: address, reachable: oldValue, isDisabled: _isDisabled) + prevIsOptedIn = calculateIsOptedIn(reachable: oldValue, isDisabled: _isDisabled) prevSubscriptionState = OSPushSubscriptionState(id: subscriptionId, token: address, optedIn: prevIsOptedIn) case .isDisabled(let oldValue): - prevIsEnabled = calculateIsEnabled(address: address, accepted: _accepted, isDisabled: oldValue) - prevIsOptedIn = calculateIsOptedIn(accepted: _accepted, isDisabled: oldValue) + prevIsEnabled = calculateIsEnabled(address: address, reachable: _reachable, isDisabled: oldValue) + prevIsOptedIn = calculateIsOptedIn(reachable: _reachable, isDisabled: oldValue) prevSubscriptionState = OSPushSubscriptionState(id: subscriptionId, token: address, optedIn: prevIsOptedIn) case .address(let oldValue): - prevIsEnabled = calculateIsEnabled(address: oldValue, accepted: _accepted, isDisabled: _isDisabled) - prevIsOptedIn = calculateIsOptedIn(accepted: _accepted, isDisabled: _isDisabled) + prevIsEnabled = calculateIsEnabled(address: oldValue, reachable: _reachable, isDisabled: _isDisabled) + prevIsOptedIn = calculateIsOptedIn(reachable: _reachable, isDisabled: _isDisabled) prevSubscriptionState = OSPushSubscriptionState(id: subscriptionId, token: oldValue, optedIn: prevIsOptedIn) } - let newIsOptedIn = calculateIsOptedIn(accepted: _accepted, isDisabled: _isDisabled) + let newIsOptedIn = calculateIsOptedIn(reachable: _reachable, isDisabled: _isDisabled) - let newIsEnabled = calculateIsEnabled(address: address, accepted: _accepted, isDisabled: _isDisabled) + let newIsEnabled = calculateIsEnabled(address: address, reachable: _reachable, isDisabled: _isDisabled) if prevIsEnabled != newIsEnabled { self.set(property: "enabled", newValue: newIsEnabled) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift index a55189c1f..c1322d0c2 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift @@ -229,7 +229,7 @@ class OSUserExecutor { type: type, address: address, subscriptionId: subModel["id"] as? String, - accepted: true, + reachable: true, isDisabled: false, changeNotifier: OSEventProducer()), hydrating: true ) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 9cf3d80a7..b9444d581 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -128,7 +128,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { private let _mockUser = OSUserInternalImpl( identityModel: OSIdentityModel(aliases: nil, changeNotifier: OSEventProducer()), propertiesModel: OSPropertiesModel(changeNotifier: OSEventProducer()), - pushSubscriptionModel: OSSubscriptionModel(type: .push, address: nil, subscriptionId: nil, accepted: false, isDisabled: true, changeNotifier: OSEventProducer())) + pushSubscriptionModel: OSSubscriptionModel(type: .push, address: nil, subscriptionId: nil, reachable: false, isDisabled: true, changeNotifier: OSEventProducer())) @objc public var requiresUserAuth = false @@ -387,14 +387,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { func createDefaultPushSubscription() -> OSSubscriptionModel { let sharedUserDefaults = OneSignalUserDefaults.initShared() - let accepted = OSNotificationsManager.currentPermissionState.accepted + let reachable = OSNotificationsManager.currentPermissionState.reachable let token = sharedUserDefaults.getSavedString(forKey: OSUD_PUSH_TOKEN, defaultValue: nil) let subscriptionId = sharedUserDefaults.getSavedString(forKey: OSUD_PUSH_SUBSCRIPTION_ID, defaultValue: nil) return OSSubscriptionModel(type: .push, address: token, subscriptionId: subscriptionId, - accepted: accepted, + reachable: reachable, isDisabled: false, changeNotifier: OSEventProducer()) } @@ -600,7 +600,7 @@ extension OneSignalUserManagerImpl: OSUser { type: .email, address: email, subscriptionId: nil, - accepted: true, + reachable: true, isDisabled: false, changeNotifier: OSEventProducer() ) @@ -632,7 +632,7 @@ extension OneSignalUserManagerImpl: OSUser { type: .sms, address: number, subscriptionId: nil, - accepted: true, + reachable: true, isDisabled: false, changeNotifier: OSEventProducer() ) @@ -739,10 +739,10 @@ extension OneSignalUserManagerImpl: OneSignalNotificationsDelegate { user.pushSubscriptionModel.address = pushToken } - public func setAccepted(_ inAccepted: Bool) { + public func setReachable(_ inReachable: Bool) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } - user.pushSubscriptionModel._accepted = inAccepted + user.pushSubscriptionModel._reachable = inReachable } } From 1171f7fffa49c60bdfbb4dab9871093a0a7da571 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 01:36:18 -0800 Subject: [PATCH 17/27] dev app remove code that was for testing only --- iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index f3a571201..7e3343121 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -131,13 +131,7 @@ - (IBAction)sendTagButton:(id)sender { } - (IBAction)getInfoButton:(id)sender { - NSLog(@"💛 Dev App: get User and Device information"); - [OneSignalUserManagerImpl.sharedInstance internalDumpInfo]; - NSLog(@"💛 Dev App: OneSignal.Notifications permission: %d", [OneSignal.Notifications permission]); - NSLog(@"💛 Dev App: OneSignal.Notifications.canRequestPermission: %d", [OneSignal.Notifications canRequestPermission]); - [OneSignal internalDumpInfo]; - NSLog(@"💛 Dev App: getPrivacyConsent: %d", OneSignal.getPrivacyConsent); - NSLog(@"💛 Dev App: requiresPrivacyConsent: %d", [OneSignal requiresPrivacyConsent]); + NSLog(@"Dev App: get User and Device information, you need to fill in"); } - (IBAction)sendTagsButton:(id)sender { From 2180aa416c481a7438345351614ee208110de2dc Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 03:54:39 -0800 Subject: [PATCH 18/27] add push subscription observer returns void * Don't return the current state when the observer is added, return void --- iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m | 4 ++-- .../OneSignalUser/Source/OneSignalUserManagerImpl.swift | 7 +++---- iOS_SDK/OneSignalSDK/Source/OSMessagingController.m | 5 +---- .../OneSignalSDK/Source/OneSignalLiveActivityController.m | 5 +---- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m index 06db3b89d..43b41951d 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m @@ -90,8 +90,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [OneSignal.Notifications setNotificationWillShowInForegroundHandler:notificationReceiverBlock]; [OneSignal.Notifications setNotificationOpenedHandler:openNotificationHandler]; - OSPushSubscriptionState* state = [OneSignal.User.pushSubscription addObserver:self]; - NSLog(@"OneSignal Demo App push subscription observer added, current state: %@", state); + [OneSignal.User.pushSubscription addObserver:self]; + NSLog(@"OneSignal Demo App push subscription observer added"); [OneSignal.Notifications addPermissionObserver:self]; diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index b9444d581..687605c0c 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -82,7 +82,7 @@ import OneSignalNotifications func optIn() func optOut() - func addObserver(_ observer: OSPushSubscriptionObserver) -> OSPushSubscriptionState? + func addObserver(_ observer: OSPushSubscriptionObserver) func removeObserver(_ observer: OSPushSubscriptionObserver) } @@ -669,12 +669,11 @@ extension OneSignalUserManagerImpl: OSUser { extension OneSignalUserManagerImpl: OSPushSubscription { - public func addObserver(_ observer: OSPushSubscriptionObserver) -> OSPushSubscriptionState? { + public func addObserver(_ observer: OSPushSubscriptionObserver) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "pushSubscription.addObserver") else { - return nil + return } self.pushSubscriptionStateChangesObserver.addObserver(observer) - return user.pushSubscriptionModel.currentPushSubscriptionState } public func removeObserver(_ observer: OSPushSubscriptionObserver) { diff --git a/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m b/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m index b3f96b69f..4a7fb41e8 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m +++ b/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m @@ -111,10 +111,7 @@ + (void)removeInstance { + (void)start { OSMessagingController *shared = OSMessagingController.sharedInstance; - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunused-variable" - OSPushSubscriptionState *_ = [OneSignalUserManagerImpl.sharedInstance addObserver:shared]; - #pragma clang diagnostic pop + [OneSignalUserManagerImpl.sharedInstance addObserver:shared]; } static BOOL _isInAppMessagingPaused = false; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalLiveActivityController.m b/iOS_SDK/OneSignalSDK/Source/OneSignalLiveActivityController.m index b6eb321b4..bcede0a02 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalLiveActivityController.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalLiveActivityController.m @@ -79,10 +79,7 @@ + (OneSignalLiveActivityController *)sharedInstance { + (void)initialize { subscriptionId = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId; OneSignalLiveActivityController *shared = OneSignalLiveActivityController.sharedInstance; - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunused-variable" - OSPushSubscriptionState *_ = [OneSignalUserManagerImpl.sharedInstance addObserver:shared]; - #pragma clang diagnostic pop + [OneSignalUserManagerImpl.sharedInstance addObserver:shared]; } - (void)onOSPushSubscriptionChangedWithStateChanges:(OSPushSubscriptionStateChanges * _Nonnull)stateChanges { From 664c1cdb3b7bbf01f260904fc11adb5e1e6fb571 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 10:39:21 -0800 Subject: [PATCH 19/27] rename setVisualLevel to setAlertLevel * Swift automatically renamed setAlertLevel to just `setAlert` so must refine name for swift. --- iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m | 2 +- iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m | 2 +- iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h | 4 ++-- iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m | 8 ++++---- iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift | 6 ++++++ 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m index 43b41951d..f6233e6eb 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m @@ -51,7 +51,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( NSLog(@"Bundle URL: %@", [[NSBundle mainBundle] bundleURL]); [OneSignal.Debug setLogLevel:ONE_S_LL_VERBOSE]; - [OneSignal.Debug setVisualLevel:ONE_S_LL_NONE]; + [OneSignal.Debug setAlertLevel:ONE_S_LL_NONE]; [OneSignal initialize:[AppDelegate getOneSignalAppId] withLaunchOptions:launchOptions]; diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m index 97c051370..86bdb05c9 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m @@ -52,7 +52,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( NSLog(@"Bundle URL: %@", [[NSBundle mainBundle] bundleURL]); [OneSignal.Debug setLogLevel:ONE_S_LL_VERBOSE]; - [OneSignal.Debug setVisualLevel:ONE_S_LL_NONE]; + [OneSignal.Debug setAlertLevel:ONE_S_LL_NONE]; _notificationDelegate = [OneSignalNotificationCenterDelegate new]; id openNotificationHandler = ^(OSNotificationOpenedResult *result) { diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h index 2f36f30c3..ffd52d5a8 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h @@ -38,12 +38,12 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) { @protocol OSDebug + (void)setLogLevel:(ONE_S_LOG_LEVEL)logLevel; -+ (void)setVisualLevel:(ONE_S_LOG_LEVEL)visualLogLevel; ++ (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel NS_REFINED_FOR_SWIFT; @end @interface OneSignalLog : NSObject + (Class)Debug; + (void)setLogLevel:(ONE_S_LOG_LEVEL)logLevel; -+ (void)setVisualLevel:(ONE_S_LOG_LEVEL)visualLogLevel; ++ (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel; + (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message; @end diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m index d12f5bb5e..ec830df0c 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m @@ -32,7 +32,7 @@ @implementation OneSignalLog static ONE_S_LOG_LEVEL _nsLogLevel = ONE_S_LL_WARN; -static ONE_S_LOG_LEVEL _visualLogLevel = ONE_S_LL_NONE; +static ONE_S_LOG_LEVEL _alertLogLevel = ONE_S_LL_NONE; + (Class)Debug { return self; @@ -42,8 +42,8 @@ + (void)setLogLevel:(ONE_S_LOG_LEVEL)nsLogLevel { _nsLogLevel = nsLogLevel; } -+ (void)setVisualLevel:(ONE_S_LOG_LEVEL)visualLogLevel { - _visualLogLevel = visualLogLevel; ++ (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel { + _alertLogLevel = logLevel; } + (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message { @@ -79,7 +79,7 @@ void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message) { if (logLevel <= _nsLogLevel) NSLog(@"%@", [levelString stringByAppendingString:message]); - if (logLevel <= _visualLogLevel) { + if (logLevel <= _alertLogLevel) { [[OSDialogInstanceManager sharedInstance] presentDialogWithTitle:levelString withMessage:message withActions:nil cancelTitle:NSLocalizedString(@"Close", @"Close button") withActionCompletion:nil]; } } diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift b/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift index 5ab4b6318..dcac11504 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift @@ -75,6 +75,12 @@ public extension OneSignal { } } +public extension OSDebug { + static func setAlertLevel(_ logLevel: ONE_S_LOG_LEVEL) { + __setAlert(logLevel) + } +} + public extension OSInAppMessages { static var Paused: Bool { get { From 6ceb44c58b012ae45c13536f5f756bb727a486aa Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 11:39:19 -0800 Subject: [PATCH 20/27] Update MIGRATION_GUIDE.md --- MIGRATION_GUIDE.md | 99 ++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index a66e85d57..c4faeae02 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -143,11 +143,15 @@ The current device’s push subscription can be retrieved via: **Objective-C** ```objc - id pushSubscription = OneSignal.User.pushSubscription; + OneSignal.User.pushSubscription.id; + OneSignal.User.pushSubscription.token; + OneSignal.User.pushSubscription.optedIn; ``` **Swift** ```swift - let pushSubscription: OSPushSubscription = OneSignal.User.pushSubscription + OneSignal.User.pushSubscription.id + OneSignal.User.pushSubscription.token + OneSignal.User.pushSubscription.optedIn ``` ### **Opting In and Out of Push Notifications** @@ -186,12 +190,12 @@ Email and/or SMS subscriptions can be added or removed via the following methods // Add email subscription [OneSignal.User addEmail:@"customer@company.com"]; // Remove previously added email subscription - BOOL success = [OneSignal.User removeEmail:@"customer@company.com"]; + [OneSignal.User removeEmail:@"customer@company.com"]; // Add SMS subscription - [OneSignal.User addSmsNumber:@"+15558675309"]; + [OneSignal.User addSms:@"+15558675309"]; // Remove previously added SMS subscription - BOOL succss = [OneSignal.User removeSmsNumber:@"+15558675309"]; + [OneSignal.User removeSms:@"+15558675309"]; ``` **Swift** @@ -199,12 +203,12 @@ Email and/or SMS subscriptions can be added or removed via the following methods // Add email subscription OneSignal.User.addEmail("customer@company.com") // Remove previously added email subscription - let success: Bool = OneSignal.User.removeEmail("customer@company.com") + OneSignal.User.removeEmail("customer@company.com") // Add SMS subscription - OneSignal.User.addSmsNumber("+15558675309") + OneSignal.User.addSms("+15558675309") // Remove previously added SMS subscription - let success: Bool = OneSignal.User.removeSmsNumber("+15558675309") + OneSignal.User.removeSms("+15558675309") ``` # API Reference @@ -291,15 +295,15 @@ The User name space is accessible via `OneSignal.User` and provides access to us | **Swift** | **Objective-C** | **Description** | | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `OneSignal.User.setLanguage("en")` | `[OneSignal.User setLanguage:@"en"]` | *Set the 2-character language for this user.* | -| `let pushSubscription: OSPushSubscription = OneSignal.User.pushSubscription` | `id pushSubscription = OneSignal.User.pushSubscription` | *The push subscription associated to the current user.* | +| `let pushSubscriptionProperty = OneSignal.User.pushSubscription.` | `id pushSubscriptionProperty = OneSignal.User.pushSubscription.` | *The push subscription associated to the current user.* | | `OneSignal.User.addAlias(label: "ALIAS_LABEL", id: "ALIAS_ID")` | `[OneSignal.User addAliasWithLabel:@"ALIAS_LABEL" id:@"ALIAS_ID"]` | *Set an alias for the current user. If this alias label already exists on this user, it will be overwritten with the new alias id.* | | `OneSignal.User.addAliases(["ALIAS_LABEL_01": "ALIAS_ID_01", "ALIAS_LABEL_02": "ALIAS_ID_02"])` | `[OneSignal.User addAliases:@{@"ALIAS_LABEL_01": @"ALIAS_ID_01", @"ALIAS_LABEL_02": @"ALIAS_ID_02"}]` | *Set aliases for the current user. If any alias already exists, it will be overwritten to the new values.* | | `OneSignal.User.removeAlias("ALIAS_LABEL")` | `[OneSignal.User removeAlias:@"ALIAS_LABEL"]` | *Remove an alias from the current user.* | | `OneSignal.User.removeAliases(["ALIAS_LABEL_01", "ALIAS_LABEL_02"])` | `[OneSignal.User removeAliases:@[@"ALIAS_LABEL_01", @"ALIAS_LABEL_02"]]` | *Remove aliases from the current user.* | | `OneSignal.User.addEmail("customer@company.com")` | `[OneSignal.User addEmail:@"customer@company.com"]` | *Add a new email subscription to the current user.* | -| `let success: Bool = OneSignal.User.removeEmail("customer@company.com")` | `BOOL success = [OneSignal.User removeEmail:@"customer@company.com"]` | *Remove an email subscription from the current user. Returns `false` if the specified email does not exist on the user within the SDK, and no request will be made.* | -| `OneSignal.User.addSmsNumber("+15558675309")` | `[OneSignal.User addSmsNumber:@"+15558675309"]` | *Add a new SMS subscription to the current user.* | -| `let success: Bool = OneSignal.User.removeSmsNumber("+15558675309")` | `BOOL success = [OneSignal.User removeSmsNumber:@"+15558675309"]` | *Remove an SMS subscription from the current user. Returns `false` if the specified SMS number does not exist on the user within the SDK, and no request will be made.* | +| `OneSignal.User.removeEmail("customer@company.com")` | `[OneSignal.User removeEmail:@"customer@company.com"]` | *Remove an email subscription from the current user. Returns `false` if the specified email does not exist on the user within the SDK, and no request will be made.* | +| `OneSignal.User.addSms("+15558675309")` | `[OneSignal.User addSms:@"+15558675309"]` | *Add a new SMS subscription to the current user.* | +| `OneSignal.User.removeSms("+15558675309")` | `[OneSignal.User removeSms:@"+15558675309"]` | *Remove an SMS subscription from the current user. Returns `false` if the specified SMS number does not exist on the user within the SDK, and no request will be made.* | | `OneSignal.User.addTag(key: "KEY", value: "VALUE")` | `[OneSignal.User addTagWithKey:@"KEY" value:@"VALUE"]` | *Add a tag for the current user. Tags are key:value pairs used as building blocks for targeting specific users and/or personalizing messages. If the tag key already exists, it will be replaced with the value provided here.* | | `OneSignal.User.addTags(["KEY_01": "VALUE_01", "KEY_02": "VALUE_02"])` | `[OneSignal.User addTags:@{@"KEY_01": @"VALUE_01", @"KEY_02": @"VALUE_02"}]` | *Add multiple tags for the current user. Tags are key:value pairs used as building blocks for targeting specific users and/or personalizing messages. If the tag key already exists, it will be replaced with the value provided here.* | | `OneSignal.User.removeTag("KEY")` | `[OneSignal.User removeTag:@"KEY"]` | *Remove the data tag with the provided key from the current user.* | @@ -319,7 +323,7 @@ The Push Subscription name space is accessible via `OneSignal.User.pushSubscript | `let optedIn: Bool = OneSignal.User.pushSubscription.optedIn` | `BOOL optedIn = OneSignal.User.pushSubscription.optedIn` | *Gets a boolean value indicating whether the current user is opted in to push notifications. This returns `true` when the app has notifications permission and `optedOut` is called. ***Note:*** Does not take into account the existence of the subscription ID and push token. This boolean may return `true` but push notifications may still not be received by the user.* | | `OneSignal.User.pushSubscription.optIn()` | `[OneSignal.User.pushSubscription optIn]` | *Call this method to receive push notifications on the device or to resume receiving of push notifications after calling `optOut`. If needed, this method will prompt the user for push notifications permission.* | | `OneSignal.User.pushSubscription.optOut()` | `[OneSignal.User.pushSubscription optOut]` | *If at any point you want the user to stop receiving push notifications on the current device (regardless of system-level permission status), you can call this method to opt out.* | -| `addObserver(_ observer: OSPushSubscriptionObserver) → OSPushSubscriptionState?`

***See below for usage*** | `(OSPushSubscriptionState * _Nullable)addObserver:(id _Nonnull)observer`

***See below for usage*** | *The `OSPushSubscriptionObserver.onOSPushSubscriptionChanged` method will be fired on the passed-in object when the push subscription changes. This method returns the current `OSPushSubscriptionState` at the time of adding this observer.* | +| `addObserver(_ observer: OSPushSubscriptionObserver)`

***See below for usage*** | `(void)addObserver:(id _Nonnull)observer`

***See below for usage*** | *The `OSPushSubscriptionObserver.onOSPushSubscriptionChanged` method will be fired on the passed-in object when the push subscription changes. This method returns the current `OSPushSubscriptionState` at the time of adding this observer.* | | `removeObserver(_ observer: OSPushSubscriptionObserver)`

***See below for usage*** | `(void)removeObserver:(id _Nonnull)observer`

***See below for usage*** | *Remove a push subscription observer that has been previously added.* | ### Push Subscription Observer @@ -338,7 +342,7 @@ Any object implementing the `OSPushSubscriptionObserver` protocol can be added a - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Add your AppDelegate as an observer - OSPushSubscriptionState* state = [OneSignal.User.pushSubscription addObserver:self]; + [OneSignal.User.pushSubscription addObserver:self]; } // Add this new method @@ -359,7 +363,7 @@ Any object implementing the `OSPushSubscriptionObserver` protocol can be added a func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Add your AppDelegate as an observer, and the current OSPushSubscriptionState will be returned - let state: OSPushSubscriptionState? = OneSignal.User.pushSubscription.addObserver(self as OSPushSubscriptionObserver) + OneSignal.User.pushSubscription.addObserver(self as OSPushSubscriptionObserver) print("Current pushSubscriptionState: \n\(state)") } @@ -464,26 +468,22 @@ Any object implementing the `OSPermissionObserver` protocol can be added as an o } // Add this new method - - (void)onOSPermissionChanged:(OSPermissionStateChanges*)stateChanges { - // Example of detecting anwsering the permission prompt - if (stateChanges.from.status == OSNotificationPermissionNotDetermined) { - if (stateChanges.to.status == OSNotificationPermissionAuthorized) - NSLog(@"Thanks for accepting notifications!"); - else if (stateChanges.to.status == OSNotificationPermissionDenied) - NSLog(@"Notifications not accepted. You can turn them on later under your iOS settings."); + - (void)onOSPermissionChanged:(OSPermissionState*)state { + // Example of detecting the curret permission + if (state.permission == true) { + NSLog(@"Device has permission to display notifications"); + } else { + NSLog(@"Device does not have permission to display notifications"); } // prints out all properties - NSLog(@"PermissionStateChanges:\n%@", stateChanges); + NSLog(@"PermissionState:\n%@", state); } // Output: /* - Thanks for accepting notifications! - PermissionStateChanges: - , - to: - > + Device has permission to display notifications + PermissionState: + */ @end @@ -498,33 +498,28 @@ Any object implementing the `OSPermissionObserver` protocol can be added as an o class AppDelegate: UIResponder, UIApplicationDelegate, OSPermissionObserver { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - // Add your AppDelegate as an observer - OneSignal.Notifications.addPermissionObserver(self as OSPermissionObserver) - } - - // Add this new method - func onOSPermissionChanged(_ stateChanges: OSPermissionStateChanges) { - // Example of detecting answering the permission prompt - if stateChanges.from.status == OSNotificationPermission.notDetermined { - if stateChanges.to.status == OSNotificationPermission.authorized { - print("Thanks for accepting notifications!") - } else if stateChanges.to.status == OSNotificationPermission.denied { - print("Notifications not accepted. You can turn them on later under your iOS settings.") - } + // Add your AppDelegate as an observer + OneSignal.Notifications.addPermissionObserver(self as OSPermissionObserver) + } + + // Add this new method + func onOSPermissionChanged(_ state: OSPermissionState) { + // Example of detecting the curret permission + if state.permission == true { + print("Device has permission to display notifications") + } else { + print("Device does not have permission to display notifications") + } + // prints out all properties + print("PermissionState: \n\(state)") } - // prints out all properties - print("PermissionStateChanges: \n\(stateChanges)") - } } // Output: /* - Thanks for accepting notifications! - PermissionStateChanges: - , - to: - > + Device has permission to display notifications + PermissionState: + */ // Remove the observer @@ -722,7 +717,7 @@ The Debug namespace is accessible via `OneSignal.Debug` and provide access to de | **Swift** | **Objective-C** | **Description** | | ------------------------------------------ | ------------------------------------------------ | ---------------------------------------------------------------------------------- | | `OneSignal.Debug.setLogLevel(.LL_VERBOSE)` | `[OneSignal.Debug setLogLevel:ONE_S_LL_VERBOSE]` | *Sets the log level the OneSignal SDK should be writing to the Xcode log.* | -| `OneSignal.Debug.setVisualLevel(.LL_NONE)` | `[OneSignal.Debug setVisualLevel:ONE_S_LL_NONE]` | *Sets the logging level to show as alert dialogs.* | +| `OneSignal.Debug.setAlertLevel(.LL_NONE)` | `[OneSignal.Debug setAlertLevel:ONE_S_LL_NONE]` | *Sets the logging level to show as alert dialogs.* | # Glossary From 6e1ba68b01cf90b03f72a3a641004498b8c7a941 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 01:32:32 -0800 Subject: [PATCH 21/27] small bug fixes * Fix OSRemoteParamController bug where it is nil * Fix removeAliases bug --- .../Source/RemoteParameters/OSRemoteParamController.m | 2 ++ .../OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift | 4 +--- .../Source/OSSubscriptionOperationExecutor.swift | 2 +- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/RemoteParameters/OSRemoteParamController.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/RemoteParameters/OSRemoteParamController.m index 1ff59d123..627da5f1c 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/RemoteParameters/OSRemoteParamController.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/RemoteParameters/OSRemoteParamController.m @@ -35,6 +35,8 @@ @implementation OSRemoteParamController static OSRemoteParamController *_sharedController; + (OSRemoteParamController *)sharedController { + if (!_sharedController) + _sharedController = [OSRemoteParamController new]; return _sharedController; } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift index 5c402a62e..af2139327 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift @@ -82,12 +82,10 @@ class OSIdentityModel: OSModel { } func removeAliases(_ labels: [String]) { - var aliasesToSend: [String: String] = [:] for label in labels { self.aliases.removeValue(forKey: label) - aliasesToSend[label] = "" + self.set(property: "aliases", newValue: [label: ""]) } - self.set(property: "aliases", newValue: aliasesToSend) } public override func hydrateModel(_ response: [String: Any]) { diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift index 4bf7b18c0..c728e585a 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift @@ -59,7 +59,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { var requestQueue: [OSRequestCreateSubscription] = [] - if var cachedAddRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestCreateSubscription] { + if let cachedAddRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_SUBSCRIPTION_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestCreateSubscription] { // Hook each uncached Request to the model in the store for request in cachedAddRequestQueue { // 1. Hook up the subscription model diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index 2414b935e..342206940 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -519,7 +519,7 @@ + (void)startUserManager { } + (void)delayInitializationForPrivacyConsent { - [OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Delayed initialization of the OneSignal SDK until the user provides privacy consent using the consentGranted() method"]; + [OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Delayed initialization of the OneSignal SDK until the user provides privacy consent using the setPrivacyConsent() method"]; delayedInitializationForPrivacyConsent = true; _delayedInitParameters = [[DelayedConsentInitializationParameters alloc] initWithLaunchOptions:launchOptions withAppId:appId]; // Init was not successful, set appId back to nil From f9ea2be38c08bc948ef4cef4ff25ed95f6515821 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 01:33:09 -0800 Subject: [PATCH 22/27] fix sending location - We were previously sending `lat` and `long` in 2 separate update requests. - While the backend was returning success for these 2 requests, it wasn't actually updating and it seems we need to send both in one request --- .../Source/OSPropertiesModel.swift | 31 ++++++++++++++----- .../Source/OSUserInternalImpl.swift | 3 +- .../OneSignalUser/Source/OSUserRequests.swift | 8 ++++- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModel.swift index 073760168..8ab2b47bc 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModel.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModel.swift @@ -45,6 +45,27 @@ struct OSPropertiesDeltas { } } +// Both lat and long must exist to be accepted by the server +class OSLocationPoint: NSObject, NSCoding { + let lat: Float + let long: Float + + init(lat: Float, long: Float) { + self.lat = lat + self.long = long + } + + public func encode(with coder: NSCoder) { + coder.encode(lat, forKey: "lat") + coder.encode(long, forKey: "long") + } + + public required init?(coder: NSCoder) { + self.lat = coder.decodeFloat(forKey: "lat") + self.long = coder.decodeFloat(forKey: "long") + } +} + class OSPropertiesModel: OSModel { var language: String? { didSet { @@ -52,15 +73,9 @@ class OSPropertiesModel: OSModel { } } - var lat: Float? { - didSet { - self.set(property: "lat", newValue: lat) - } - } - - var long: Float? { + var location: OSLocationPoint? { didSet { - self.set(property: "long", newValue: long) + self.set(property: "location", newValue: location) } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserInternalImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserInternalImpl.swift index 890411dd9..75b02deb6 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserInternalImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserInternalImpl.swift @@ -125,8 +125,7 @@ class OSUserInternalImpl: NSObject, OSUserInternal { func setLocation(lat: Float, long: Float) { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OneSignal.User setLocation called with lat: \(lat) long: \(long)") - propertiesModel.lat = lat - propertiesModel.long = long + propertiesModel.location = OSLocationPoint(lat: lat, long: long) } // MARK: - Language diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift index c1322d0c2..0ebfadb0b 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserRequests.swift @@ -900,8 +900,14 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { self.stringDescription = "OSRequestUpdateProperties with properties: \(properties) deltas: \(String(describing: deltas)) refreshDeviceMetadata: \(String(describing: refreshDeviceMetadata))" super.init() + var propertiesObject = properties + if let location = propertiesObject["location"] as? OSLocationPoint { + propertiesObject["lat"] = location.lat + propertiesObject["long"] = location.long + propertiesObject.removeValue(forKey: "location") + } var params: [String: Any] = [:] - params["properties"] = properties + params["properties"] = propertiesObject params["refresh_device_metadata"] = refreshDeviceMetadata if let deltas = deltas { params["deltas"] = deltas From 6921ef6991be261a862307353c2b84d5a49d3317 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 01:33:21 -0800 Subject: [PATCH 23/27] update `Notifications.clearAll()` method to really clear all --- .../OneSignalNotifications/OSNotificationsManager.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m index fccb328a7..4f8286123 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m @@ -337,6 +337,8 @@ + (void)presentAppSettings { } + (void)clearAll { + [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; + // TODO: Determine if we also need to call clearBadgeCount [self clearBadgeCount:false]; } From 7af3f09e41a3544feb81d3d317f560e495c7ac2b Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 03:33:05 -0800 Subject: [PATCH 24/27] saving push token locally doesn't need privacy consent * If the app starts up without privacy consent, and later grant it, we never get the push token * Save the push token, this is ok without privacy consent as we don't send it anywhere but keep locally in the SDK. --- .../OneSignalNotifications/OSNotificationsManager.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m index 4f8286123..00d55dab2 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m +++ b/iOS_SDK/OneSignalSDK/OneSignalNotifications/OSNotificationsManager.m @@ -371,9 +371,6 @@ + (BOOL)registerForAPNsToken { + (void)didRegisterForRemoteNotifications:(UIApplication *)app deviceToken:(NSData *)inDeviceToken { - if ([OSPrivacyConsentController shouldLogMissingPrivacyConsentErrorWithMethodName:nil]) - return; - let parsedDeviceToken = [NSString hexStringFromData:inDeviceToken]; [OneSignalLog onesignalLog:ONE_S_LL_INFO message: [NSString stringWithFormat:@"Device Registered with Apple: %@", parsedDeviceToken]]; From 1824c2fbd1c6f2c01c5a13558cb63a0bdb41b851 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 03:46:11 -0800 Subject: [PATCH 25/27] logging out of anonymous user is a no-op * As discussed in design forum, calling logout multiple times should result in a no-op. * Note that states can carry over. --- .../OneSignalUser/Source/OneSignalUserManagerImpl.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 687605c0c..7984fcbf9 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -322,10 +322,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { } /** - The SDK needs to have a user at all times, so this method will create a new anonymous user. + The SDK needs to have a user at all times, so this method will create a new anonymous user. If the current user is already anonymous, calling `logout` results in a no-op. */ @objc public func logout() { + guard user.identityModel.externalId != nil else { + OneSignalLog.onesignalLog(.LL_DEBUG, message: "OneSignal.User logout called, but the user is currently anonymous, so not logging out.") + return + } prepareForNewUser() _user = nil createUserIfNil() From 944d7585d9822d18c88c2716c26b14746115c414 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 12:22:48 -0800 Subject: [PATCH 26/27] Update MIGRATION_GUIDE.md --- MIGRATION_GUIDE.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index c4faeae02..a39c95df9 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,4 +1,4 @@ -

OneSignal iOS SDK v5.0.0-alpha-02 Migration Guide

+

OneSignal iOS SDK v5.0.0-beta-01 Migration Guide

![OneSignal Omni Channel Banner](https://user-images.githubusercontent.com/11739227/208625336-d28c8d01-a7cf-4f8e-9643-ac8d1948e9ae.png) @@ -8,7 +8,7 @@ In this release, we are making a significant shift from a device-centered model To facilitate this change, the `externalId` approach for identifying users is being replaced by the `login` and `logout` methods. In addition, the SDK now makes use of namespaces such as `User`, `Notifications`, and `InAppMessages` to better separate code. -The iOS SDK is making the jump from `v3` to `v5`, in order to align across OneSignal’s suite of client SDKs. This guide will walk you through the iOS SDK `5.0.0-alpha-02` changes as a result of this shift. +The iOS SDK is making the jump from `v3` to `v5`, in order to align across OneSignal’s suite of client SDKs. This guide will walk you through the iOS SDK `5.0.0-beta-01` changes as a result of this shift. # Overview @@ -60,10 +60,10 @@ As mentioned above, the iOS SDK is making the jump from `v3` to `v5`, in order t ``` ### Option 1. Swift Package Manager -Update the version of the OneSignal-XCFramework your application uses to `5.0.0-alpha-02`. In addition, the Package Product name has been changed from `OneSignal` to `OneSignalFramework`. See [the existing installation instructions](https://documentation.onesignal.com/docs/swift-package-manager-setup). +Update the version of the OneSignal-XCFramework your application uses to `5.0.0-beta-01`. In addition, the Package Product name has been changed from `OneSignal` to `OneSignalFramework`. See [the existing installation instructions](https://documentation.onesignal.com/docs/swift-package-manager-setup). ### Option 2. CocoaPods -Update the version of the OneSignalXCFramework your application uses to `5.0.0-alpha-02`. Other than updating the import statement above, there are no additional changes needed to import the OneSignal SDK in your Xcode project. See [the existing installation instructions](https://documentation.onesignal.com/docs/ios-sdk-setup#step-3-import-the-onesignal-sdk-into-your-xcode-project). +Update the version of the OneSignalXCFramework your application uses to `5.0.0-beta-01`. Other than updating the import statement above, there are no additional changes needed to import the OneSignal SDK in your Xcode project. See [the existing installation instructions](https://documentation.onesignal.com/docs/ios-sdk-setup#step-3-import-the-onesignal-sdk-into-your-xcode-project). # API Changes ## Namespaces @@ -213,7 +213,7 @@ Email and/or SMS subscriptions can be added or removed via the following methods # API Reference -Below is a comprehensive reference to the `5.0.0-alpha-02` OneSignal SDK. +Below is a comprehensive reference to the `5.0.0-beta-01` OneSignal SDK. ## OneSignal @@ -727,14 +727,12 @@ The Debug namespace is accessible via `OneSignal.Debug` and provide access to de # Limitations -- Recommend using only in development and staging environments for Alpha releases -- Aliases will be available in a future release +- Recommend using only in development and staging environments for Beta releases - Outcomes will be available in a future release -- Users are deleted when the last Subscription (push, email, or sms) is removed - Any `User` namespace calls must be invoked **after** initialization. Example: `OneSignal.User.addTag("tag", "2")` # Known issues - User properties may not update correctly when Subscriptions are transferred - Please report any issues you find with this - Identity Verification - - We will be introducing JWT in a follow up Alpha or Beta release + - We will be introducing JWT in a follow up Beta release From d592eeff35b277d7eb4909f57a0ae2152f34ce41 Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 3 Feb 2023 12:30:47 -0800 Subject: [PATCH 27/27] Allow adding push subscription observer before privacy consent * Even though the User namespace is invoked when calling `OneSignal.User.pushSubscription.addObserver()`, this is ok. We want them to be able to add it at startup before consent may be granted. --- .../OneSignalUser/Source/OneSignalUserManagerImpl.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 7984fcbf9..d2c9104ff 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -674,9 +674,7 @@ extension OneSignalUserManagerImpl: OSUser { extension OneSignalUserManagerImpl: OSPushSubscription { public func addObserver(_ observer: OSPushSubscriptionObserver) { - guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "pushSubscription.addObserver") else { - return - } + // This is a method in the User namespace that doesn't require privacy consent first self.pushSubscriptionStateChangesObserver.addObserver(observer) }