diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index a66e85d57..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 @@ -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,17 +203,17 @@ 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 -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 @@ -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 @@ -732,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 diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m index b6821b64b..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]; @@ -84,19 +84,14 @@ - (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]; [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]; @@ -122,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/OneSignalDevApp/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard index 1e045c93f..14cbdab24 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 +708,14 @@ - + + + + + + @@ -588,25 +723,27 @@ - + - + - + + - + + + + - - - + 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/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..7e3343121 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 addSms:sms]; +} + +- (IBAction)removeSmsButton:(id)sender { + NSString *sms = self.smsTextField.text; + NSLog(@"Dev App: Removing sms: %@", sms); + [OneSignal.User removeSms: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,8 @@ - (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, you need to fill in"); } - (IBAction)sendTagsButton:(id)sender { @@ -147,7 +173,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 +196,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 +255,25 @@ - (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]; +} + +- (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 diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/AppDelegate.m index 59e6ffb7d..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) { @@ -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/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 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 diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 9c7f45231..d741d72ae 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -214,21 +214,8 @@ 9D59C2F92321C7780008ECCF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D3300F923149DAE000F0A83 /* CoreGraphics.framework */; }; 9DDFEEF223189C0800EAE0BB /* OneSignalViewHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3300F423145AF3000F0A83 /* OneSignalViewHelper.h */; }; 9DDFEEF323189C0E00EAE0BB /* OneSignalViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3300F223145AF3000F0A83 /* OneSignalViewHelper.m */; }; - A63E9E3826742B4100EA273E /* LanguageProviderAppDefined.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519A92669747B00AED40E /* LanguageProviderAppDefined.m */; }; - A63E9E3926742B4200EA273E /* LanguageProviderAppDefined.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519A92669747B00AED40E /* LanguageProviderAppDefined.m */; }; - A63E9E3A26742B4600EA273E /* LanguageProviderDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519AC2669749100AED40E /* LanguageProviderDevice.m */; }; - A63E9E3B26742B4700EA273E /* LanguageProviderDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519AC2669749100AED40E /* LanguageProviderDevice.m */; }; - A63E9E3C26742B5F00EA273E /* LanguageContext.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519A52669614A00AED40E /* LanguageContext.m */; }; - A63E9E3D26742B6000EA273E /* LanguageContext.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519A52669614A00AED40E /* LanguageContext.m */; }; - A63E9E3E26742C1000EA273E /* LanguageContext.h in Headers */ = {isa = PBXBuildFile; fileRef = A6B519A42669614A00AED40E /* LanguageContext.h */; }; - A63E9E3F26742C1400EA273E /* LanguageProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = A6B519A7266964BA00AED40E /* LanguageProvider.h */; }; - A63E9E4026742C1600EA273E /* LanguageProviderAppDefined.h in Headers */ = {isa = PBXBuildFile; fileRef = A6B519A82669747B00AED40E /* LanguageProviderAppDefined.h */; }; - A63E9E4126742C1800EA273E /* LanguageProviderDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = A6B519AB2669749100AED40E /* LanguageProviderDevice.h */; }; A662399326850DDE00D52FD8 /* LanguageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A662399026850DDE00D52FD8 /* LanguageTest.m */; }; A66239952686612F00D52FD8 /* OneSignalFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 912411F01E73342200E41FD7 /* OneSignalFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6B519A62669614A00AED40E /* LanguageContext.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519A52669614A00AED40E /* LanguageContext.m */; }; - A6B519AA2669747B00AED40E /* LanguageProviderAppDefined.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519A92669747B00AED40E /* LanguageProviderAppDefined.m */; }; - A6B519AD2669749100AED40E /* LanguageProviderDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B519AC2669749100AED40E /* LanguageProviderDevice.m */; }; CA08FC871FE99BB4004C445F /* OneSignalClientOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = CA08FC831FE99BB4004C445F /* OneSignalClientOverrider.m */; }; CA1A6E6920DC2E31001C41B9 /* OneSignalDialogController.h in Headers */ = {isa = PBXBuildFile; fileRef = CA1A6E6720DC2E31001C41B9 /* OneSignalDialogController.h */; }; CA1A6E6A20DC2E31001C41B9 /* OneSignalDialogController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA1A6E6820DC2E31001C41B9 /* OneSignalDialogController.m */; }; @@ -869,13 +856,6 @@ 9D348538233D2DCF00EB81C9 /* OneSignalLocationOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalLocationOverrider.h; sourceTree = ""; }; 9D348539233D2E3600EB81C9 /* OneSignalLocationOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalLocationOverrider.m; sourceTree = ""; }; A662399026850DDE00D52FD8 /* LanguageTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LanguageTest.m; sourceTree = ""; }; - A6B519A42669614A00AED40E /* LanguageContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LanguageContext.h; sourceTree = ""; }; - A6B519A52669614A00AED40E /* LanguageContext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LanguageContext.m; sourceTree = ""; }; - A6B519A7266964BA00AED40E /* LanguageProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LanguageProvider.h; sourceTree = ""; }; - A6B519A82669747B00AED40E /* LanguageProviderAppDefined.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LanguageProviderAppDefined.h; sourceTree = ""; }; - A6B519A92669747B00AED40E /* LanguageProviderAppDefined.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LanguageProviderAppDefined.m; sourceTree = ""; }; - A6B519AB2669749100AED40E /* LanguageProviderDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LanguageProviderDevice.h; sourceTree = ""; }; - A6B519AC2669749100AED40E /* LanguageProviderDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LanguageProviderDevice.m; sourceTree = ""; }; CA08FC821FE99BB4004C445F /* OneSignalClientOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalClientOverrider.h; sourceTree = ""; }; CA08FC831FE99BB4004C445F /* OneSignalClientOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalClientOverrider.m; sourceTree = ""; }; CA1A6E6720DC2E31001C41B9 /* OneSignalDialogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalDialogController.h; sourceTree = ""; }; @@ -1428,7 +1408,6 @@ isa = PBXGroup; children = ( DE7D18D92703B3EA002D3A5D /* Requests */, - A63E9E42267A847800EA273E /* Language */, 7AAA606B2485D0D70004FADE /* Migration */, 7A674F172360D7DB001F9ACD /* OnFocus */, 454F94F31FAD263300D74CCF /* Model */, @@ -1479,20 +1458,6 @@ name = Categories; sourceTree = ""; }; - A63E9E42267A847800EA273E /* Language */ = { - isa = PBXGroup; - children = ( - A6B519A42669614A00AED40E /* LanguageContext.h */, - A6B519A52669614A00AED40E /* LanguageContext.m */, - A6B519A7266964BA00AED40E /* LanguageProvider.h */, - A6B519A82669747B00AED40E /* LanguageProviderAppDefined.h */, - A6B519A92669747B00AED40E /* LanguageProviderAppDefined.m */, - A6B519AB2669749100AED40E /* LanguageProviderDevice.h */, - A6B519AC2669749100AED40E /* LanguageProviderDevice.m */, - ); - name = Language; - sourceTree = ""; - }; CA6B3CB621B5CF2600AA6A65 /* Model */ = { isa = PBXGroup; children = ( @@ -1998,12 +1963,9 @@ 7AAA60662485D0310004FADE /* OSMigrationController.h in Headers */, CA47439D2190FEA80020DC8C /* OSTrigger.h in Headers */, 9DDFEEF223189C0800EAE0BB /* OneSignalViewHelper.h in Headers */, - A63E9E4126742C1800EA273E /* LanguageProviderDevice.h in Headers */, - A63E9E3F26742C1400EA273E /* LanguageProvider.h in Headers */, A66239952686612F00D52FD8 /* OneSignalFramework.h in Headers */, 7A93269325AF4E6700BBEC27 /* OSPendingCallbacks.h in Headers */, 944F7ED1296F9F0700AEBA54 /* OneSignalLiveActivityController.h in Headers */, - A63E9E3E26742C1000EA273E /* LanguageContext.h in Headers */, CA4742E4218B8FF30020DC8C /* OSTriggerController.h in Headers */, DE7D18EC2703B5AA002D3A5D /* OSInAppMessagingRequests.h in Headers */, DE16C14724D3727200670EFA /* OneSignalLifecycleObserver.h in Headers */, @@ -2017,7 +1979,6 @@ 912412351E73342200E41FD7 /* OneSignalTrackIAP.h in Headers */, 7AECE59423674AA700537907 /* OSAttributedFocusTimeProcessor.h in Headers */, 912412311E73342200E41FD7 /* OneSignalTracker.h in Headers */, - A63E9E4026742C1600EA273E /* LanguageProviderAppDefined.h in Headers */, 3C79BEB9293DC88F0034CB10 /* OneSignalInAppMessaging.h in Headers */, 9124123D1E73342200E41FD7 /* UIApplicationDelegate+OneSignal.h in Headers */, 7AF9865324451F3900C36EAE /* OSFocusCallParams.h in Headers */, @@ -2575,7 +2536,6 @@ 7A1F2D8F2406EFC5007799A9 /* OSInAppMessageTag.m in Sources */, 7A72EB0E23E252C200B4D50F /* OSInAppMessageDisplayStats.m in Sources */, 9124121E1E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */, - A6B519A62669614A00AED40E /* LanguageContext.m in Sources */, 912412471E73369600E41FD7 /* OneSignalHelper.m in Sources */, 7A880F312404AE7B0081F5E8 /* OSInAppMessagePushPrompt.m in Sources */, CA8E19062193C76D009DA223 /* OSInAppMessagingHelpers.m in Sources */, @@ -2583,7 +2543,6 @@ DE7D18DF2703B49B002D3A5D /* OSFocusRequests.m in Sources */, 7A674F1B2360D82E001F9ACD /* OSBaseFocusTimeProcessor.m in Sources */, DE16C14424D3724700670EFA /* OneSignalLifecycleObserver.m in Sources */, - A6B519AA2669747B00AED40E /* LanguageProviderAppDefined.m in Sources */, 9D3300F523145AF3000F0A83 /* OneSignalViewHelper.m in Sources */, 9124123E1E73342200E41FD7 /* UIApplicationDelegate+OneSignal.m in Sources */, CA47439E2190FEA80020DC8C /* OSTrigger.m in Sources */, @@ -2591,7 +2550,6 @@ CA1A6E7020DC2E73001C41B9 /* OneSignalDialogRequest.m in Sources */, 912412321E73342200E41FD7 /* OneSignalTracker.m in Sources */, CA4742E5218B8FF30020DC8C /* OSTriggerController.m in Sources */, - A6B519AD2669749100AED40E /* LanguageProviderDevice.m in Sources */, CAB269E021B2038B00F8A43C /* OSInAppMessageBridgeEvent.m in Sources */, DE7D18EE2703B5B9002D3A5D /* OSInAppMessagingRequests.m in Sources */, DE367CC724EEF2BE00165207 /* OSInAppMessagePage.m in Sources */, @@ -2654,11 +2612,9 @@ 9DDFEEF323189C0E00EAE0BB /* OneSignalViewHelper.m in Sources */, DE16C14524D3724700670EFA /* OneSignalLifecycleObserver.m in Sources */, CA47439F2190FEA80020DC8C /* OSTrigger.m in Sources */, - A63E9E3826742B4100EA273E /* LanguageProviderAppDefined.m in Sources */, CAB4112A20852E4C005A70D1 /* DelayedConsentInitializationParameters.m in Sources */, 9124123F1E73342200E41FD7 /* UIApplicationDelegate+OneSignal.m in Sources */, 7AECE59F23675F6300537907 /* OSFocusTimeProcessorFactory.m in Sources */, - A63E9E3A26742B4600EA273E /* LanguageProviderDevice.m in Sources */, DEC08B012947D4E900C81DA3 /* OneSignalSwiftInterface.swift in Sources */, CA1A6E7120DC2E73001C41B9 /* OneSignalDialogRequest.m in Sources */, CA4742E6218B8FF30020DC8C /* OSTriggerController.m in Sources */, @@ -2666,7 +2622,6 @@ 912412331E73342200E41FD7 /* OneSignalTracker.m in Sources */, DE367CC824EEF2BE00165207 /* OSInAppMessagePage.m in Sources */, DE7D18EF2703B5B9002D3A5D /* OSInAppMessagingRequests.m in Sources */, - A63E9E3C26742B5F00EA273E /* LanguageContext.m in Sources */, CACBAAA2218A6243000ACAA5 /* OSInAppMessageInternal.m in Sources */, 7A674F1C2360D82E001F9ACD /* OSBaseFocusTimeProcessor.m in Sources */, 7AFE856C2368DDB80091D6A5 /* OSFocusCallParams.m in Sources */, @@ -2702,7 +2657,6 @@ CA85C15320604AEA003AB529 /* RequestTests.m in Sources */, 9D34853A233D2E3600EB81C9 /* OneSignalLocationOverrider.m in Sources */, 912412381E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */, - A63E9E3D26742B6000EA273E /* LanguageContext.m in Sources */, 7ABAF9D22457C3650074DFA0 /* CommonAsserts.m in Sources */, 03217239238278EB004F0E85 /* DelayedSelectors.m in Sources */, CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */, @@ -2736,7 +2690,6 @@ 03866CBD2378A33B0009C1D8 /* OutcomeIntegrationTests.m in Sources */, DE20426024E21C2C00350E4F /* UIApplication+OneSignal.m in Sources */, CA36F35B21C33A2500300C77 /* OSInAppMessageController.m in Sources */, - A63E9E3926742B4200EA273E /* LanguageProviderAppDefined.m in Sources */, 03389F691FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m in Sources */, 7ABAF9D62457D3FF0074DFA0 /* ChannelTrackersTests.m in Sources */, 4529DED51FA823B900CEAB1D /* TestHelperFunctions.m in Sources */, @@ -2784,7 +2737,6 @@ 03866CC12378A67B0009C1D8 /* RestClientAsserts.m in Sources */, 7ADF891C230DB5BD0054E0D6 /* UnitTestAppDelegate.m in Sources */, 4529DEF01FA8433500CEAB1D /* NSLocaleOverrider.m in Sources */, - A63E9E3B26742B4700EA273E /* LanguageProviderDevice.m in Sources */, CA1A6E7220DC2E73001C41B9 /* OneSignalDialogRequest.m in Sources */, 7A880F332404AE7B0081F5E8 /* OSInAppMessagePushPrompt.m in Sources */, ); diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OneSignalClient.m index 16b127cd9..9c38f0c7f 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; @@ -375,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) 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/OneSignalCore/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h index 68165d972..882022816 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 @@ -284,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 @@ -310,7 +303,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/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/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/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 e29b8d1c0..00d55dab2 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) @@ -336,6 +337,8 @@ + (void)presentAppSettings { } + (void)clearAll { + [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; + // TODO: Determine if we also need to call clearBadgeCount [self clearBadgeCount:false]; } @@ -368,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]]; @@ -421,11 +421,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]; } @@ -455,8 +455,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.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..0ae54bf99 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,37 +198,21 @@ - (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]; } // 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]; } } @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/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/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/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) + } } } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModel.swift index d2f42eef5..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? { + var location: OSLocationPoint? { didSet { - self.set(property: "lat", newValue: lat) - } - } - - var long: Float? { - didSet { - self.set(property: "long", newValue: long) + self.set(property: "location", newValue: location) } } @@ -71,6 +86,16 @@ class OSPropertiesModel: OSModel { // We seem to lose access to this init() in superclass after adding init?(coder: NSCoder) override init(changeNotifier: OSEventProducer) { super.init(changeNotifier: changeNotifier) + self.language = getPreferredLanguage() + } + + private func getPreferredLanguage() -> String { + let preferredLanguages = NSLocale.preferredLanguages + if !preferredLanguages.isEmpty { + return preferredLanguages[0] + } else { + return DEFAULT_LANGUAGE + } } override func encode(with coder: NSCoder) { 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) + } } } } 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/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionOperationExecutor.swift index bc9d43c20..c728e585a 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 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 (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) + } } } } 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 1017ea1a6..0ebfadb0b 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 { - // log error + 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 { + 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) } } @@ -127,7 +229,7 @@ class OSUserExecutor { type: type, address: address, subscriptionId: subModel["id"] as? String, - accepted: true, + reachable: true, isDisabled: false, changeNotifier: OSEventProducer()), hydrating: true ) @@ -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,84 +332,133 @@ 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() } } } - 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, - identityModel: nil, - retainPreviousUser: retainPreviousUser // Need to update logic to determine this, for now, default to true + aliasId: aliasId ) - 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() } } } @@ -288,6 +466,7 @@ class OSUserExecutor { // MARK: - User Request Classes protocol OSUserRequest: OneSignalRequest, NSCoding { + var sentToClient: Bool { get set } func prepareForExecution() -> Bool } @@ -300,13 +479,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 { @@ -319,13 +500,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 @@ -341,22 +519,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") @@ -377,6 +571,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 @@ -390,18 +585,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 @@ -478,40 +673,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 @@ -528,6 +714,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 { @@ -535,9 +723,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 @@ -546,6 +734,7 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { } class OSRequestAddAliases: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription @@ -603,6 +792,7 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { } class OSRequestRemoveAlias: OneSignalRequest, OSUserRequest { + var sentToClient = false let stringDescription: String override var description: String { return stringDescription @@ -660,15 +850,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(), @@ -687,7 +881,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 @@ -706,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 @@ -752,6 +952,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 @@ -822,38 +1023,26 @@ 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 - } + // TODO: self.addJWTHeader(identityModel: identityModel) ?? + return true } else { self.path = "" // self.path is non-nil, so set to empty string return false @@ -861,30 +1050,25 @@ class OSRequestTransferSubscription: OneSignalRequest, OSUserRequest { } /** - Must pass either an `identityModel` or an Alias pair to identify the User. - If `retainPreviousUser` flag is not passed in, it defaults to `true`. + Must pass an Alias pair to identify the User. */ init( subscriptionModel: OSSubscriptionModel, - aliasLabel: String?, - aliasId: String?, - identityModel: OSIdentityModel?, - retainPreviousUser: Bool? + aliasLabel: String, + aliasId: String ) { self.subscriptionModel = subscriptionModel - self.identityModel = identityModel 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 } 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") @@ -895,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 @@ -903,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 @@ -919,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 @@ -997,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 diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 477a4b2fb..d2c9104ff 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -60,12 +60,12 @@ 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?) // TODO: why optional? As a remove? + func setLanguage(_ language: String) // JWT Token Expire typealias OSJwtCompletionBlock = (_ newJwtToken: String) -> Void typealias OSJwtExpiredHandler = (_ externalId: String, _ completion: OSJwtCompletionBlock) -> Void @@ -82,7 +82,7 @@ import OneSignalNotifications func optIn() func optOut() - func addObserver(_ observer: OSPushSubscriptionObserver) -> OSPushSubscriptionState? + func addObserver(_ observer: OSPushSubscriptionObserver) func removeObserver(_ observer: OSPushSubscriptionObserver) } @@ -98,6 +98,10 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { return _user?.pushSubscriptionModel.subscriptionId } + @objc public var language: String? { + return _user?.propertiesModel.language + } + private var hasCalledStart = false private var jwtExpiredHandler: OSJwtExpiredHandler? @@ -124,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 @@ -190,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() @@ -213,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 @@ -316,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() @@ -381,14 +391,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()) } @@ -458,6 +468,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 } @@ -593,7 +604,7 @@ extension OneSignalUserManagerImpl: OSUser { type: .email, address: email, subscriptionId: nil, - accepted: true, + reachable: true, isDisabled: false, changeNotifier: OSEventProducer() ) @@ -605,16 +616,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 } @@ -625,7 +636,7 @@ extension OneSignalUserManagerImpl: OSUser { type: .sms, address: number, subscriptionId: nil, - accepted: true, + reachable: true, isDisabled: false, changeNotifier: OSEventProducer() ) @@ -637,31 +648,34 @@ 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?) { + public func setLanguage(_ language: String) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "setLanguage") else { return } + + if language == "" { + OneSignalLog.onesignalLog(.LL_ERROR, message: "OneSignal.User.setLanguage cannot be called with an empty language code.") + return + } + user.setLanguage(language) } } extension OneSignalUserManagerImpl: OSPushSubscription { - public func addObserver(_ observer: OSPushSubscriptionObserver) -> OSPushSubscriptionState? { - guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "pushSubscription.addObserver") else { - return nil - } + public func addObserver(_ observer: OSPushSubscriptionObserver) { + // This is a method in the User namespace that doesn't require privacy consent first self.pushSubscriptionStateChangesObserver.addObserver(observer) - return user.pushSubscriptionModel.currentPushSubscriptionState } public func removeObserver(_ observer: OSPushSubscriptionObserver) { @@ -726,10 +740,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 } } diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageContext.h b/iOS_SDK/OneSignalSDK/Source/LanguageContext.h deleted file mode 100644 index 8200d0a2b..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageContext.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#import -#import "LanguageProvider.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface LanguageContext : NSObject - -- (void)setStrategy:(NSObject*)strategy; - -@property (nonatomic, nonnull)NSString* language; - -@end - -NS_ASSUME_NONNULL_END diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageContext.m b/iOS_SDK/OneSignalSDK/Source/LanguageContext.m deleted file mode 100644 index 1d8057f35..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageContext.m +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#import "LanguageContext.h" -#import "LanguageProvider.h" -#import "OneSignalHelper.h" -#import "LanguageProviderDevice.h" -#import "LanguageProviderAppDefined.h" - -@implementation LanguageContext { - - NSObject *strategy; -} - -- (instancetype)init { - self = [super init]; - let languageAppDefined = [OneSignalUserDefaults.initStandard getSavedStringForKey:OSUD_LANGUAGE defaultValue:nil]; - if (languageAppDefined == nil) { - strategy = [LanguageProviderDevice new]; - } else { - strategy = [LanguageProviderAppDefined new]; - } - return self; -} - -- (void)setStrategy:(NSObject*)newStrategy { - strategy = newStrategy; -} - -- (NSString *)language { - return strategy.language; -} -@end diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageProvider.h b/iOS_SDK/OneSignalSDK/Source/LanguageProvider.h deleted file mode 100644 index 92cfed3fc..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageProvider.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -@protocol LanguageProvider - -@property (readonly, nonnull)NSString* language; - -@end diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageProviderAppDefined.h b/iOS_SDK/OneSignalSDK/Source/LanguageProviderAppDefined.h deleted file mode 100644 index 24deadbbc..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageProviderAppDefined.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#import -#import "LanguageProvider.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface LanguageProviderAppDefined : NSObject - -@property (nonnull)NSString* language; - -@end - -NS_ASSUME_NONNULL_END diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageProviderAppDefined.m b/iOS_SDK/OneSignalSDK/Source/LanguageProviderAppDefined.m deleted file mode 100644 index 0d6e701a6..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageProviderAppDefined.m +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#import "LanguageProviderAppDefined.h" -#import "OneSignalHelper.h" -#import "LanguageContext.h" - -@implementation LanguageProviderAppDefined - -- (void)setLanguage:(NSString *)language { - [OneSignalUserDefaults.initStandard saveStringForKey:OSUD_LANGUAGE withValue:language]; -} - -- (NSString *)language { - return [OneSignalUserDefaults.initStandard getSavedStringForKey:OSUD_LANGUAGE defaultValue:DEFAULT_LANGUAGE]; -} - -@end diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageProviderDevice.h b/iOS_SDK/OneSignalSDK/Source/LanguageProviderDevice.h deleted file mode 100644 index 7a24a1760..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageProviderDevice.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#import -#import "LanguageProvider.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface LanguageProviderDevice : NSObject - -@property (readonly, nonnull)NSString* language; - -@end - -NS_ASSUME_NONNULL_END diff --git a/iOS_SDK/OneSignalSDK/Source/LanguageProviderDevice.m b/iOS_SDK/OneSignalSDK/Source/LanguageProviderDevice.m deleted file mode 100644 index 28a47566a..000000000 --- a/iOS_SDK/OneSignalSDK/Source/LanguageProviderDevice.m +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Modified MIT License - * - * Copyright 2021 OneSignal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * 1. The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 2. All copies of substantial portions of the Software may only be used in connection - * with services provided by OneSignal. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#import "LanguageProviderDevice.h" -#import "OneSignalHelper.h" -#import "LanguageContext.h" - -@implementation LanguageProviderDevice - -- (NSString *)language { - let preferredLanguages = [NSLocale preferredLanguages]; - if (preferredLanguages && preferredLanguages.count > 0) - return [preferredLanguages objectAtIndex:0]; - return DEFAULT_LANGUAGE; -} - -@end diff --git a/iOS_SDK/OneSignalSDK/Source/OSInAppMessageController.m b/iOS_SDK/OneSignalSDK/Source/OSInAppMessageController.m index 281b36c00..5aa037244 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSInAppMessageController.m +++ b/iOS_SDK/OneSignalSDK/Source/OSInAppMessageController.m @@ -27,6 +27,7 @@ #import "OSInAppMessageController.h" #import +#import #import "OSInAppMessagingDefines.h" #import "OSInAppMessagingRequests.h" @@ -38,7 +39,7 @@ - (void)loadMessageHTMLContentWithResult:(OSResultSuccessBlock _Nullable)success if (!variantId) { if (failureBlock) - failureBlock([NSError errorWithDomain:@"onesignal" code:0 userInfo:@{@"error" : [NSString stringWithFormat:@"Unable to find variant ID for languages (%@) for message ID: %@", NSLocale.preferredLanguages, self.messageId]}]); + failureBlock([NSError errorWithDomain:@"onesignal" code:0 userInfo:@{@"error" : [NSString stringWithFormat:@"Unable to find variant ID for languages (%@) for message ID: %@", OneSignalUserManagerImpl.sharedInstance.language, self.messageId]}]); return; } @@ -62,20 +63,16 @@ variant over lower platforms (ie. 'all') even if they have a matching language. */ - (NSString * _Nullable)variantId { - let isoLanguageCodes = [NSLocale preferredLanguages]; + // we only want the first two characters, ie. "en-US" we want "en" + NSString *userLanguageCode = [OneSignalUserManagerImpl.sharedInstance.language substringToIndex:2]; NSString *variantId; for (NSString *type in PREFERRED_VARIANT_ORDER) { if (self.variants[type]) { - for (NSString *code in isoLanguageCodes) { - // we only want the first two characters, ie. "en-US" we want "en" - let isoLanguageCode = [code substringToIndex:2]; - - if (self.variants[type][isoLanguageCode]) { - variantId = self.variants[type][isoLanguageCode]; - break; - } + if (self.variants[type][userLanguageCode]) { + variantId = self.variants[type][userLanguageCode]; + break; } if (!variantId && self.variants[type][@"default"]) { diff --git a/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m b/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m index dd8626782..4a7fb41e8 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m +++ b/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m @@ -30,7 +30,6 @@ #import "UIApplication+OneSignal.h" // Previously imported via "OneSignalHelper.h" #import "NSDateFormatter+OneSignal.h" // Previously imported via "OneSignalHelper.h" #import -#import "OneSignalInternal.h" #import "OSInAppMessageAction.h" #import "OSInAppMessageController.h" #import "OSInAppMessagePrompt.h" @@ -112,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; @@ -317,7 +313,7 @@ - (void)onDidDismissInAppMessage:(OSInAppMessageInternal *)message { - (void)presentInAppMessage:(OSInAppMessageInternal *)message { if (!message.variantId) { - let errorMessage = [NSString stringWithFormat:@"Attempted to display a message with a nil variantId. Current preferred language is %@, supported message variants are %@", NSLocale.preferredLanguages, message.variants]; + let errorMessage = [NSString stringWithFormat:@"Attempted to display a message with a nil variantId. Current preferred language is %@, supported message variants are %@", OneSignalUserManagerImpl.sharedInstance.language, message.variants]; [OneSignalLog onesignalLog:ONE_S_LL_ERROR message:errorMessage]; return; } diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index c83d6aa43..342206940 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -76,9 +76,6 @@ #import "OneSignalLifecycleObserver.h" -#import "LanguageProviderAppDefined.h" -#import "LanguageContext.h" - #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" @@ -124,17 +121,6 @@ + (DelayedConsentInitializationParameters *)delayedInitParameters { // Set when the app is launched static NSDate *sessionLaunchTime; -static LanguageContext* languageContext; - -// 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 @@ -203,9 +189,7 @@ + (void)clearStatics { [OSNotificationsManager clearStatics]; registeredWithApple = false; - - _permissionStateChangesObserver = nil; - + _downloadedParameters = false; _didCallDownloadParameters = false; @@ -535,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 @@ -574,9 +558,6 @@ + (void)init { } // Now really initializing the SDK! - - // TODO: Language move to user? - languageContext = [LanguageContext new]; [self initInAppLaunchURLSettings:appSettings]; 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 { 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 {