diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 710b81c..80243de 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -65,6 +65,14 @@ Once (or if) the user is no longer identifiable in your app (i.e. they logged ou Logging out has the affect of reverting to a “device-scoped” user, which is the new owner of the device’s push subscription. +To observe changes to the onesignalId or externalId you can add a custom method to the event: + + OneSignal.User.Changed += User_Changed; + + private void User_Changed(object sender, OneSignalSDK.DotNet.Core.User.UserStateChangedEventArgs e) { + var user = e.State.Current; + ... + } ## Subscriptions @@ -157,7 +165,10 @@ The user name space is accessible via `OneSignal.User` and provides access to us | **C#** | **Description** | | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IPushSubscription PushSubscription { get; }` | *The push subscription associated to the current user.* | -| `string Language { set; }` | *Set the 2-character language either as a detected language or explicitly set for this user.* | +| `string Language { set; }` | *Set the 2-character language either as a detected language or explicitly set for this user.* +| `string OneSignalId` | *The UUID generated by OneSignal to represent a user, null if this is currently unavailable.* | +| `string ExternalId` | *The External ID is OneSignal's default and recommended alias label. This should be the mainidentifier you use to identify users. It is set when calling the [OneSignal.login] method. Return null if this is currently unavailable.* +| `event EventHandler Changed` | *Adds a change event that will run whenever the onesignalId or externalId has been changed.* | `void AddAlias(string label, string id)` | *Set an alias for the current user. If this alias already exists it will be overwritten.* | | `void AddAliases(IDictionary aliases)` | *Set aliases for the current user. If any alias already exists it will be overwritten.* | | `void RemoveAlias(string label)` | *Remove an alias from the current user.* | diff --git a/OneSignalSDK.DotNet.Android.Core.Binding/Transforms/Metadata.xml b/OneSignalSDK.DotNet.Android.Core.Binding/Transforms/Metadata.xml index 3fff94e..162a33e 100644 --- a/OneSignalSDK.DotNet.Android.Core.Binding/Transforms/Metadata.xml +++ b/OneSignalSDK.DotNet.Android.Core.Binding/Transforms/Metadata.xml @@ -8,6 +8,7 @@ Com.OneSignal.Android.Session Com.OneSignal.Android.User Com.OneSignal.Android.User.Subscriptions + Com.OneSignal.Android.User.State diff --git a/OneSignalSDK.DotNet.Android/AndroidUserManager.cs b/OneSignalSDK.DotNet.Android/AndroidUserManager.cs index 4634017..5954786 100644 --- a/OneSignalSDK.DotNet.Android/AndroidUserManager.cs +++ b/OneSignalSDK.DotNet.Android/AndroidUserManager.cs @@ -15,11 +15,33 @@ public string Language public IPushSubscription PushSubscription { get; } = new AndroidPushSubscription(); + private InternalUserChangedHandler? _userChangedHandler; + public void Initialize() { + _userChangedHandler = new InternalUserChangedHandler(this); + OneSignalNative.User.AddObserver(_userChangedHandler); ((AndroidPushSubscription)PushSubscription).Initialize(); } + public string OneSignalId + { + get { + string id = OneSignalNative.User.OnesignalId; + return string.IsNullOrEmpty(id)? null : id; + } + } + + public string ExternalId + { + get { + string id = OneSignalNative.User.ExternalId; + return string.IsNullOrEmpty(id)? null : id; + } + } + + public event EventHandler Changed; + public void AddAlias(string label, string id) => OneSignalNative.User.AddAlias(label, id); public void AddAliases(IDictionary aliases) => OneSignalNative.User.AddAliases(aliases); public void RemoveAlias(string label) => OneSignalNative.User.RemoveAlias(label); @@ -36,6 +58,35 @@ public void Initialize() public void RemoveTag(string key) => OneSignalNative.User.RemoveTag(key); public void RemoveTags(params string[] keys) => OneSignalNative.User.RemoveTags(keys); public IDictionary GetTags() => OneSignalNative.User.Tags; + + private sealed class InternalUserState : IUserState + { + public string OneSignalId { get; } + + public string ExternalId { get; } + + public InternalUserState(string onesignalId, string externalId) + { + OneSignalId = onesignalId; + ExternalId = externalId; + } + } + + private class InternalUserChangedHandler : Java.Lang.Object, Com.OneSignal.Android.User.State.IUserStateObserver + { + private AndroidUserManager _manager; + public InternalUserChangedHandler(AndroidUserManager manager) + { + _manager = manager; + } + + public void OnUserStateChange(Com.OneSignal.Android.User.State.UserChangedState state) + { + var current = new InternalUserState(state.Current.OnesignalId, state.Current.ExternalId); + var userChangedState = new UserChangedState(current); + _manager.Changed?.Invoke(_manager, new UserStateChangedEventArgs(userChangedState)); + } + } } public class AndroidPushSubscription : IPushSubscription @@ -99,5 +150,6 @@ public void OnPushSubscriptionChange(Com.OneSignal.Android.User.Subscriptions.Pu _manager.Changed?.Invoke(_manager, new PushSubscriptionChangedEventArgs(changedState)); } } + } } diff --git a/OneSignalSDK.DotNet.Core/User/IUserManager.cs b/OneSignalSDK.DotNet.Core/User/IUserManager.cs index 31d0cac..ce0172b 100644 --- a/OneSignalSDK.DotNet.Core/User/IUserManager.cs +++ b/OneSignalSDK.DotNet.Core/User/IUserManager.cs @@ -35,6 +35,24 @@ public interface IUserManager /// IPushSubscription PushSubscription { get; } + /// + /// The UUID generated by OneSignal to represent a user, null if this is currently unavailable. + /// + string OneSignalId { get; } + + /// + /// The External ID is OneSignal's default and recommended alias label. This should be the main + /// identifier you use to identify users. It is set when calling the [OneSignal.login] method. + /// + /// This is null if the External ID has not been set. + /// + string ExternalId { get; } + + /// + /// When onesignalId or externalId has changed + /// + event EventHandler Changed; + /// /// Set an alias for the current user. If this alias already exists it will be overwritten. /// diff --git a/OneSignalSDK.DotNet.Core/User/IUserState.cs b/OneSignalSDK.DotNet.Core/User/IUserState.cs new file mode 100644 index 0000000..efce1a9 --- /dev/null +++ b/OneSignalSDK.DotNet.Core/User/IUserState.cs @@ -0,0 +1,20 @@ +using System; + +namespace OneSignalSDK.DotNet.Core.User +{ + public interface IUserState { + /// + /// The unique identifier for your OneSignal account. This will be an empty string until the + /// user has been successfully logged in on the backend and assigned an ID. + /// Use [Changed] to be notified when the [OneSignalId] has been successfully assigned. + /// + string OneSignalId { get; } + + /// + /// The external identifier that you use to identify users. Use [Changed] to be notified + /// when the [ExternalId] has been successfully assigned. This will be an empty string if no + /// external identifier has been assigned to the associated [OneSignalId]. + /// + string ExternalId { get; } + } +} \ No newline at end of file diff --git a/OneSignalSDK.DotNet.Core/User/UserStateChangedEventArgs.cs b/OneSignalSDK.DotNet.Core/User/UserStateChangedEventArgs.cs new file mode 100644 index 0000000..8d903d5 --- /dev/null +++ b/OneSignalSDK.DotNet.Core/User/UserStateChangedEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace OneSignalSDK.DotNet.Core.User +{ + /// + /// Several states associated with the SDK can be changed in and outside of the application. + /// + public class UserStateChangedEventArgs : EventArgs + { + public UserChangedState State { get; } + + public UserStateChangedEventArgs(UserChangedState state) { + State = state; + } + } + + public sealed class UserChangedState { + public IUserState Current { get; } + + public UserChangedState(IUserState state) { + Current = state; + } + } +} diff --git a/OneSignalSDK.DotNet.iOS.Binding/ApiDefinitions.cs b/OneSignalSDK.DotNet.iOS.Binding/ApiDefinitions.cs index 18f9550..85b2872 100644 --- a/OneSignalSDK.DotNet.iOS.Binding/ApiDefinitions.cs +++ b/OneSignalSDK.DotNet.iOS.Binding/ApiDefinitions.cs @@ -444,6 +444,60 @@ interface OSPushSubscription void RemoveObserver(OSPushSubscriptionObserver observer); } + // @interface OSUserState : NSObject + [BaseType (typeof(NSObject), Name = "_TtC13OneSignalUser11OSUserState")] + [DisableDefaultCtor] + interface OSUserState + { + // @property (readonly, copy, nonatomic) NSString * _Nullable onesignalId; + [NullAllowed, Export ("onesignalId")] + string OnesignalId { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable externalId; + [NullAllowed, Export ("externalId")] + string ExternalId { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nonnull description; + [Export ("description")] + string Description { get; } + + // -(NSDictionary * _Nonnull)jsonRepresentation __attribute__((warn_unused_result(""))); + [Export ("jsonRepresentation")] + //[Verify (MethodToProperty)] + NSDictionary JsonRepresentation { get; } + } + + // @interface OSUserChangedState : NSObject + [BaseType (typeof(NSObject), Name = "_TtC13OneSignalUser18OSUserChangedState")] + [DisableDefaultCtor] + interface OSUserChangedState + { + // @property (readonly, nonatomic, strong) OSUserState * _Nonnull current; + [Export ("current", ArgumentSemantic.Strong)] + OSUserState Current { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nonnull description; + [Export ("description")] + string Description { get; } + + // -(NSDictionary * _Nonnull)jsonRepresentation __attribute__((warn_unused_result(""))); + [Export ("jsonRepresentation")] + //[Verify (MethodToProperty)] + NSDictionary JsonRepresentation { get; } + } + + // @protocol OSUserStateObserver + [Protocol (Name = "_TtP13OneSignalUser19OSUserStateObserver_")] + [Model] + [BaseType(typeof(NSObject))] + interface OSUserStateObserver + { + // @required -(void)onUserStateDidChangeWithState:(OSUserChangedState * _Nonnull)state; + //[Abstract] + [Export ("onUserStateDidChangeWithState:")] + void OnUserStateDidChangeWithState (OSUserChangedState state); + } + // @protocol OSUser [Protocol(Name = "_TtP13OneSignalUser6OSUser_")] [BaseType(typeof(NSObject))] @@ -454,6 +508,26 @@ interface OSUser [Export ("pushSubscription", ArgumentSemantic.Strong)] OSPushSubscription PushSubscription { get; } + // @required @property (readonly, copy, nonatomic) NSString * _Nullable onesignalId; + //[Abstract] + [NullAllowed, Export ("onesignalId")] + string OnesignalId { get; } + + // @required @property (readonly, copy, nonatomic) NSString * _Nullable externalId; + //[Abstract] + [NullAllowed, Export ("externalId")] + string ExternalId { get; } + + // @required -(void)addObserver:(id _Nonnull)observer; + //[Abstract] + [Export ("addObserver:")] + void AddObserver (OSUserStateObserver observer); + + // @required -(void)removeObserver:(id _Nonnull)observer; + [Abstract] + [Export ("removeObserver:")] + void RemoveObserver (OSUserStateObserver observer); + // @required -(void)addAliasWithLabel:(NSString * _Nonnull)label id:(NSString * _Nonnull)id; //[Abstract] [Export ("addAliasWithLabel:id:")] diff --git a/OneSignalSDK.DotNet.iOS/iOSUserManager.cs b/OneSignalSDK.DotNet.iOS/iOSUserManager.cs index ae9e0da..42b0da9 100644 --- a/OneSignalSDK.DotNet.iOS/iOSUserManager.cs +++ b/OneSignalSDK.DotNet.iOS/iOSUserManager.cs @@ -18,11 +18,26 @@ public string Language public IPushSubscription PushSubscription { get; } = new iOSPushSubscription(); + private InternalUserChangedHandler _userChangedHandler; + public void Initialize() { + _userChangedHandler = new InternalUserChangedHandler(this); + OneSignalNative.User.AddObserver(_userChangedHandler); ((iOSPushSubscription)PushSubscription).Initialize(); } + public string OneSignalId + { + get => OneSignalNative.User.OnesignalId; + } + + public string ExternalId + { + get => OneSignalNative.User.ExternalId; + } + public event EventHandler? Changed; + public void AddAlias(string label, string id) => OneSignalNative.User.AddAliasWithLabel(label, id); public void AddAliases(IDictionary aliases) => OneSignalNative.User.AddAliases(NativeConversion.DictToNSDict(aliases)); public void RemoveAlias(string label) => OneSignalNative.User.RemoveAlias(label); @@ -39,6 +54,35 @@ public void Initialize() public void RemoveTag(string key) => OneSignalNative.User.RemoveTag(key); public void RemoveTags(params string[] keys) => OneSignalNative.User.RemoveTags(keys); public IDictionary GetTags() => FromNativeConversion.NSDictToPureStringDict(OneSignalNative.User.GetTags()); + + private sealed class InternalUserState : IUserState + { + public string OneSignalId { get; } + + public string ExternalId { get; } + + public InternalUserState(string onesignalId, string externalId) + { + OneSignalId = onesignalId; + ExternalId = externalId; + } + } + + private class InternalUserChangedHandler : Com.OneSignal.iOS.OSUserStateObserver + { + private iOSUserManager _manager; + public InternalUserChangedHandler(iOSUserManager manager) + { + _manager = manager; + } + + public override void OnUserStateDidChangeWithState(OSUserChangedState state) + { + var current = new InternalUserState(state.Current.OnesignalId, state.Current.ExternalId); + var userChangedState = new UserChangedState(current); + _manager.Changed?.Invoke(_manager, new UserStateChangedEventArgs(userChangedState)); + } + } } public class iOSPushSubscription : IPushSubscription diff --git a/Samples/OneSignalApp/Models/MainPageModel.cs b/Samples/OneSignalApp/Models/MainPageModel.cs index c3e554c..cb68752 100644 --- a/Samples/OneSignalApp/Models/MainPageModel.cs +++ b/Samples/OneSignalApp/Models/MainPageModel.cs @@ -5,6 +5,7 @@ using System.Windows.Input; using OneSignalSDK.DotNet; using OneSignalSDK.DotNet.Core.Debug; +using OneSignalSDK.DotNet.Core.User; using OneSignalSDK.DotNet.Core.User.Subscriptions; namespace OneSignalApp.Models @@ -190,6 +191,7 @@ public MainPageModel(Page page) OneSignal.Initialize(_appId); + OneSignal.User.Changed += User_Changed; OneSignal.User.PushSubscription.Changed += PushSubscription_Changed; OneSignal.Notifications.PermissionChanged += Notifications_PermissionChanged; OneSignal.Notifications.Clicked += Notifications_Clicked; @@ -208,6 +210,12 @@ public MainPageModel(Page page) PushSubscriptionId = OneSignal.User.PushSubscription.Id; } + private void User_Changed(object sender, OneSignalSDK.DotNet.Core.User.UserStateChangedEventArgs e) + { + var user = e.State.Current; + Debug.WriteLine($"User has changed: OneSignalId=${user.OneSignalId}, ExternalId={user.ExternalId}"); + } + private void InAppMessages_Clicked(object sender, OneSignalSDK.DotNet.Core.InAppMessages.InAppMessageClickedEventArgs e) { Debug.WriteLine($"IAM clicked: ${e.Result.ActionId}.");