Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getters and observer for onesignal ID and external ID #1344

Merged
merged 8 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ The User name space is accessible via `OneSignal.User` and provides access to us
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OneSignal.User.setLanguage("en")` | `[OneSignal.User setLanguage:@"en"]` | *Set the 2-character language for this user.* |
| `let pushSubscriptionProperty = OneSignal.User.pushSubscription.<PROPERTY>` | `id pushSubscriptionProperty = OneSignal.User.pushSubscription.<PROPERTY>` | *The push subscription associated to the current user. Please refer to the Push Subscription Namespace API below for additional details.* |
| `let id: String? = OneSignal.User.onesignalId` | `NSString* id = OneSignal.User.onesignalId` | *Returns the nullable OneSignal ID for the current user.* |
| `let id: String? = OneSignal.User.externalId` | `NSString* id = OneSignal.User.externalId` | *Returns the nullable external ID for the current user.* |
| `OneSignal.User.addObserver(_ observer: OSUserStateObserver)`<br><br>***See below for usage*** | `[OneSignal.User addObserver:self]`<br><br>***See below for usage*** | *The `OSUserStateObserver.onUserStateDidChange` method will be fired on the passed-in object when the user state changes. The User State contains the nullable onesignalId and externalId, and the observer will be fired when these values change.* |
| `OneSignal.User.removeObserver(_ observer: OSUserStateObserver)` | `[OneSignal.User removeObserver:self]` | *Remove a user state observer that has been previously added.* |
| `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.* |
Expand All @@ -335,6 +339,61 @@ The User name space is accessible via `OneSignal.User` and provides access to us
| `OneSignal.User.removeTags(["KEY_01", "KEY_02"])` | `[OneSignal.User removeTags:@[@"KEY_01", @"KEY_02"]]` | *Remove multiple tags with the provided keys from the current user.* |


### User State Observer

Any object implementing the `OSUserStateObserver` protocol can be added as an observer. You can call `removeObserver` to remove any existing listeners.

**Objective-C**
```objc
// AppDelegate.h
// Add OSUserStateObserver after UIApplicationDelegate
@interface AppDelegate : UIResponder <UIApplicationDelegate, OSUserStateObserver>
@end

// AppDelegate.m
@implementation AppDelegate

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add your AppDelegate as an observer
[OneSignal.User addObserver:self];
}

// Add this new method
- (void)onUserStateDidChangeWithState:(OSUserChangedState * _Nonnull)state {
// prints out all properties
NSLog(@"OSUserChangedState:\n%@", [state jsonRepresentation]);
NSLog(@"current externalId: %@", state.current.externalId);
NSLog(@"current onesignalId: %@", state.current.onesignalId);
}
@end

// Remove the observer
[OneSignal.User removeObserver:self];
```
**Swift**
```swift
// AppDelegate.swift
// Add OSUserStateObserver after UIApplicationDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, OSUserStateObserver {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Add your AppDelegate as an observer
OneSignal.User.addObserver(self)
}

// Add this new method
func onUserStateDidChange(state: OSUserChangedState) {
// prints out all properties
print("OSUserChangedState: \n\(state.jsonRepresentation())")
print(state.current.externalId)
print(state.current.onesignalId)
}
}

// Remove the observer
OneSignal.User.removeObserver(self)
```


## Push Subscription Namespace

Expand Down
2 changes: 1 addition & 1 deletion iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#import <UIKit/UIKit.h>
#import <OneSignalFramework/OneSignalFramework.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, OSNotificationPermissionObserver, OSInAppMessageLifecycleListener, OSPushSubscriptionObserver, OSNotificationLifecycleListener, OSInAppMessageClickListener, OSNotificationClickListener>
@interface AppDelegate : UIResponder <UIApplicationDelegate, OSNotificationPermissionObserver, OSInAppMessageLifecycleListener, OSPushSubscriptionObserver, OSNotificationLifecycleListener, OSInAppMessageClickListener, OSNotificationClickListener, OSUserStateObserver>

@property (strong, nonatomic) UIWindow *window;

Expand Down
5 changes: 5 additions & 0 deletions iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[OneSignal.User.pushSubscription addObserver:self];
NSLog(@"OneSignal Demo App push subscription observer added");

[OneSignal.User addObserver:self];
[OneSignal.Notifications addPermissionObserver:self];
[OneSignal.InAppMessages addClickListener:self];

Expand Down Expand Up @@ -107,6 +108,10 @@ - (void)onClickNotification:(OSNotificationClickEvent * _Nonnull)event {
NSLog(@"Dev App onClickNotification with event %@", [event jsonRepresentation]);
}

- (void)onUserStateDidChangeWithState:(OSUserChangedState * _Nonnull)state {
NSLog(@"Dev App onUserStateDidChangeWithState: %@", [state jsonRepresentation]);
}

#pragma mark OSInAppMessageDelegate

- (void)onClickInAppMessage:(OSInAppMessageClickEvent * _Nonnull)event {
Expand Down
4 changes: 4 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
3C47A974292642B100312125 /* OneSignalConfigManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C47A972292642B100312125 /* OneSignalConfigManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
3C47A975292642B100312125 /* OneSignalConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C47A973292642B100312125 /* OneSignalConfigManager.m */; };
3C4F9E4428A4466C009F453A /* OSOperationRepo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F9E4328A4466C009F453A /* OSOperationRepo.swift */; };
3C5117172B15C31E00563465 /* OSUserState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C5117162B15C31E00563465 /* OSUserState.swift */; };
3C789DBD293C2206004CF83D /* OSFocusInfluenceParam.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A600B432453790700514A53 /* OSFocusInfluenceParam.m */; };
3C789DBE293D8EAD004CF83D /* OSFocusInfluenceParam.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A600B41245378ED00514A53 /* OSFocusInfluenceParam.h */; settings = {ATTRIBUTES = (Public, ); }; };
3C8E6DF928A6D89E0031E48A /* OSOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8E6DF828A6D89E0031E48A /* OSOperationExecutor.swift */; };
Expand Down Expand Up @@ -744,6 +745,7 @@
3C47A972292642B100312125 /* OneSignalConfigManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalConfigManager.h; sourceTree = "<group>"; };
3C47A973292642B100312125 /* OneSignalConfigManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalConfigManager.m; sourceTree = "<group>"; };
3C4F9E4328A4466C009F453A /* OSOperationRepo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSOperationRepo.swift; sourceTree = "<group>"; };
3C5117162B15C31E00563465 /* OSUserState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSUserState.swift; sourceTree = "<group>"; };
3C8E6DF828A6D89E0031E48A /* OSOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSOperationExecutor.swift; sourceTree = "<group>"; };
3C8E6DFE28AB09AE0031E48A /* OSPropertyOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSPropertyOperationExecutor.swift; sourceTree = "<group>"; };
3C8E6E0028AC0BA10031E48A /* OSIdentityOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSIdentityOperationExecutor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1605,6 +1607,7 @@
3CE795F828DB99B500736BD4 /* OSSubscriptionModelStoreListener.swift */,
3CE795FA28DBDCE700736BD4 /* OSSubscriptionOperationExecutor.swift */,
3CA6CE0928E4F19B00CA0585 /* OSUserRequests.swift */,
3C5117162B15C31E00563465 /* OSUserState.swift */,
);
path = Source;
sourceTree = "<group>";
Expand Down Expand Up @@ -2935,6 +2938,7 @@
3C2C7DC8288F3C020020F9AE /* OSSubscriptionModel.swift in Sources */,
3CF8629E28A183F900776CA4 /* OSIdentityModel.swift in Sources */,
3CE795FB28DBDCE700736BD4 /* OSSubscriptionOperationExecutor.swift in Sources */,
3C5117172B15C31E00563465 /* OSUserState.swift in Sources */,
3CE9227A289FA88B001B1062 /* OSIdentityModelStoreListener.swift in Sources */,
DE69E19F282ED8060090BB3D /* OneSignalUser.docc in Sources */,
3CA6CE0A28E4F19B00CA0585 /* OSUserRequests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP

#define OS_ON_USER_WILL_CHANGE @"OS_ON_USER_WILL_CHANGE"

// OSID and EID snapshots during hydration
#define OS_SNAPSHOT_ONESIGNAL_ID @"OS_SNAPSHOT_ONESIGNAL_ID"
#define OS_SNAPSHOT_EXTERNAL_ID @"OS_SNAPSHOT_EXTERNAL_ID"

// Models and Model Stores
#define OS_IDENTITY_MODEL_KEY @"OS_IDENTITY_MODEL_KEY"
#define OS_IDENTITY_MODEL_STORE_KEY @"OS_IDENTITY_MODEL_STORE_KEY"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ + (void)removeInstance {

+ (void)start {
OSMessagingController *shared = OSMessagingController.sharedInstance;
[OneSignalUserManagerImpl.sharedInstance addObserver:shared];
[OneSignalUserManagerImpl.sharedInstance.pushSubscriptionImpl addObserver:shared];
}

static BOOL _isInAppMessagingPaused = false;
Expand Down
30 changes: 28 additions & 2 deletions iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,42 @@ class OSIdentityModel: OSModel {

public override func hydrateModel(_ response: [String: Any]) {
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSIdentityModel hydrateModel()")
var newOnesignalId: String?
var newExternalId: String?

for property in response {
switch property.key {
case "external_id":
aliases[OS_EXTERNAL_ID] = property.value as? String
newExternalId = property.value as? String
aliases[OS_EXTERNAL_ID] = newExternalId
case "onesignal_id":
aliases[OS_ONESIGNAL_ID] = property.value as? String
newOnesignalId = property.value as? String
aliases[OS_ONESIGNAL_ID] = newOnesignalId
default:
aliases[property.key] = property.value as? String
}
self.set(property: "aliases", newValue: aliases)
}
fireUserStateChanged(newOnesignalId: newOnesignalId, newExternalId: newExternalId)
}

/**
Fires the user observer if `onesignal_id` OR `external_id` has changed from the previous snapshot (previous hydration).
*/
private func fireUserStateChanged(newOnesignalId: String?, newExternalId: String?) {
let prevOnesignalId = OneSignalUserDefaults.initShared().getSavedString(forKey: OS_SNAPSHOT_ONESIGNAL_ID, defaultValue: nil)
let prevExternalId = OneSignalUserDefaults.initShared().getSavedString(forKey: OS_SNAPSHOT_EXTERNAL_ID, defaultValue: nil)

guard prevOnesignalId != newOnesignalId || prevExternalId != newExternalId else {
return
}

OneSignalUserDefaults.initShared().saveString(forKey: OS_SNAPSHOT_ONESIGNAL_ID, withValue: newOnesignalId)
OneSignalUserDefaults.initShared().saveString(forKey: OS_SNAPSHOT_EXTERNAL_ID, withValue: newExternalId)

let curUserState = OSUserState(onesignalId: newOnesignalId, externalId: newExternalId)
let changedState = OSUserChangedState(current: curUserState)

OneSignalUserManagerImpl.sharedInstance.userStateChangesObserver.notifyChange(changedState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,6 @@ extension OSSubscriptionModel {

// TODO: Don't fire observer until server is udated
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "firePushSubscriptionChanged from \(prevSubscriptionState.jsonRepresentation()) to \(newSubscriptionState.jsonRepresentation())")
OneSignalUserManagerImpl.sharedInstance.pushSubscriptionStateChangesObserver.notifyChange(stateChanges)
OneSignalUserManagerImpl.sharedInstance.pushSubscriptionImpl.pushSubscriptionStateChangesObserver.notifyChange(stateChanges)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ class OSUserExecutor {
if let response = response {
// Clear local data in preparation for hydration
OneSignalUserManagerImpl.sharedInstance.clearUserData()
parseFetchUserResponse(response: response, identityModel: request.identityModel, originalPushToken: OneSignalUserManagerImpl.sharedInstance.token)
parseFetchUserResponse(response: response, identityModel: request.identityModel, originalPushToken: OneSignalUserManagerImpl.sharedInstance.pushSubscriptionImpl.token)

// If this is a on-new-session's fetch user call, check that the subscription still exists
if request.onNewSession,
Expand Down
69 changes: 69 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSUserState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Modified MIT License

Copyright 2023 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.
*/

@objc
public class OSUserState: NSObject {
@objc public let onesignalId: String?
@objc public let externalId: String?

@objc public override var description: String {
return "<OSUserState: onesignalId: \(onesignalId ?? "nil"), externalId: \(externalId ?? "nil")>"
}

init(onesignalId: String?, externalId: String?) {
self.onesignalId = onesignalId
self.externalId = externalId
}

@objc public func jsonRepresentation() -> NSDictionary {
return [
"onesignalId": onesignalId ?? "",
"externalId": externalId ?? ""
]
}
}

@objc
public class OSUserChangedState: NSObject {
@objc public let current: OSUserState

@objc public override var description: String {
return "<OSUserState:\ncurrent: \(self.current)\n>"
}

init(current: OSUserState) {
self.current = current
}

@objc public func jsonRepresentation() -> NSDictionary {
return ["current": current.jsonRepresentation()]
}
}

@objc public protocol OSUserStateObserver {
@objc func onUserStateDidChange(state: OSUserChangedState)
}
Loading
Loading