Skip to content

Commit

Permalink
Add User.Changed event
Browse files Browse the repository at this point in the history
  • Loading branch information
nirinchev committed Aug 31, 2023
1 parent 2b2821f commit c0fad03
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 4 deletions.
8 changes: 4 additions & 4 deletions Realm/Realm/Handles/SessionHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ public static extern ulong register_progress_notifier(SessionHandle session,
public static extern void unregister_progress_notifier(SessionHandle session, ulong token, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_register_property_changed_callback", CallingConvention = CallingConvention.Cdecl)]
public static extern SessionNotificationToken register_property_changed_callback(IntPtr session, IntPtr managed_session_handle, out NativeException ex);
public static extern SessionNotificationToken register_property_changed_callback(SessionHandle session, IntPtr managed_session_handle, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_unregister_property_changed_callback", CallingConvention = CallingConvention.Cdecl)]
public static extern void unregister_property_changed_callback(IntPtr session, SessionNotificationToken token, out NativeException ex);
public static extern void unregister_property_changed_callback(SessionHandle session, SessionNotificationToken token, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_wait", CallingConvention = CallingConvention.Cdecl)]
public static extern void wait(SessionHandle session, IntPtr task_completion_source, ProgressDirection direction, out NativeException ex);
Expand Down Expand Up @@ -196,15 +196,15 @@ public void SubscribeNotifications(Session session)

var managedSessionHandle = GCHandle.Alloc(session, GCHandleType.Weak);
var sessionPointer = GCHandle.ToIntPtr(managedSessionHandle);
_notificationToken = NativeMethods.register_property_changed_callback(handle, sessionPointer, out var ex);
_notificationToken = NativeMethods.register_property_changed_callback(this, sessionPointer, out var ex);
ex.ThrowIfNecessary();
}

public void UnsubscribeNotifications()
{
if (_notificationToken.HasValue)
{
NativeMethods.unregister_property_changed_callback(handle, _notificationToken.Value, out var ex);
NativeMethods.unregister_property_changed_callback(this, _notificationToken.Value, out var ex);
_notificationToken = null;
ex.ThrowIfNecessary();
}
Expand Down
73 changes: 73 additions & 0 deletions Realm/Realm/Handles/SyncUserHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using Realms.Logging;
using Realms.Native;

namespace Realms.Sync
Expand All @@ -31,6 +33,9 @@ internal class SyncUserHandle : StandaloneHandle
{
private static class NativeMethods
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void UserChangedCallback(IntPtr managed_user);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_id", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_user_id(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, out NativeException ex);

Expand Down Expand Up @@ -109,13 +114,33 @@ public static extern void create_api_key(SyncUserHandle handle, AppHandle app,

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_path_for_realm", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_path_for_realm(SyncUserHandle handle, [MarshalAs(UnmanagedType.LPWStr)] string? partition, IntPtr partition_len, IntPtr buffer, IntPtr bufsize, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_register_changed_callback", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr register_changed_callback(SyncUserHandle user, IntPtr managed_user_handle, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_unregister_property_changed_callback", CallingConvention = CallingConvention.Cdecl)]
public static extern void unregister_changed_callback(SyncUserHandle user, IntPtr token, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_install_callbacks", CallingConvention = CallingConvention.Cdecl)]
public static extern void install_syncuser_callbacks(UserChangedCallback changed_callback);
}

private IntPtr _notificationToken;

[Preserve]
public SyncUserHandle(IntPtr handle) : base(handle)
{
}

public static void Initialize()
{
NativeMethods.UserChangedCallback changed = HandleUserChanged;

GCHandle.Alloc(changed);

NativeMethods.install_syncuser_callbacks(changed);
}

public string GetUserId()
{
return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) =>
Expand Down Expand Up @@ -400,5 +425,53 @@ public string GetRealmPath(string? partition = null)
return NativeMethods.get_path_for_realm(this, partition, partition.IntPtrLength(), buffer, bufferLength, out ex);
})!;
}

public void SubscribeNotifications(User user)
{
Debug.Assert(_notificationToken == IntPtr.Zero, $"{nameof(_notificationToken)} must be Zero before subscribing.");

var managedUserHandle = GCHandle.Alloc(user, GCHandleType.Weak);
var sessionPointer = GCHandle.ToIntPtr(managedUserHandle);
_notificationToken = NativeMethods.register_changed_callback(this, sessionPointer, out var ex);
ex.ThrowIfNecessary();
}

public void UnsubscribeNotifications()
{
if (_notificationToken != IntPtr.Zero)
{
NativeMethods.unregister_changed_callback(this, _notificationToken, out var ex);
_notificationToken = IntPtr.Zero;
ex.ThrowIfNecessary();
}
}

[MonoPInvokeCallback(typeof(NativeMethods.UserChangedCallback))]
private static void HandleUserChanged(IntPtr managedUserHandle)
{
try
{
if (managedUserHandle == IntPtr.Zero)
{
return;
}

var user = (User?)GCHandle.FromIntPtr(managedUserHandle).Target;
if (user is null)
{
// We're taking a weak handle to the session, so it's possible that it's been collected
return;
}

ThreadPool.QueueUserWorkItem(_ =>
{
user.RaiseChanged();
});
}
catch (Exception ex)
{
Logger.Default.Log(LogLevel.Error, $"An error has occurred while raising a property changed event: {ex}");
}
}
}
}
1 change: 1 addition & 0 deletions Realm/Realm/Native/NativeCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ internal static void Initialize()
SynchronizationContextScheduler.Initialize();
SharedRealmHandle.Initialize();
SessionHandle.Initialize();
SyncUserHandle.Initialize();
HttpClientTransport.Initialize();
AppHandle.Initialize();
SubscriptionSetHandle.Initialize();
Expand Down
36 changes: 36 additions & 0 deletions Realm/Realm/Sync/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Threading.Tasks;
using MongoDB.Bson;
Expand All @@ -34,6 +36,35 @@ namespace Realms.Sync
/// </summary>
public class User : IEquatable<User>
{
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "This is the private event - the public is uppercased.")]
private event EventHandler? _changed;

/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event EventHandler? Changed
{
add
{
if (_changed == null)
{
Handle.SubscribeNotifications(this);
}

_changed += value;
}

remove
{
_changed -= value;

if (_changed == null)
{
Handle.UnsubscribeNotifications();
}
}
}

/// <summary>
/// Gets this user's refresh token. This is the user's credential for accessing MongoDB Atlas data and should be treated as sensitive information.
/// </summary>
Expand Down Expand Up @@ -297,6 +328,11 @@ public override string ToString()
return $"User {Id}, State: {State}, Provider: {Provider}";
}

internal void RaiseChanged()
{
_changed?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// A class exposing functionality for users to manage API keys from the client. It is always scoped
/// to a particular <see cref="User"/> and can only be accessed via <see cref="ApiKeys"/>.
Expand Down
102 changes: 102 additions & 0 deletions Tests/Realm.Tests/Sync/UserManagementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using NUnit.Framework;
Expand Down Expand Up @@ -983,6 +984,107 @@ public void UserToStringOverride()
Assert.That(user.ToString(), Does.Contain(user.Provider.ToString()));
}

[Test]
public void UserLogOut_RaisesChanged()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var user = await GetUserAsync();
var tcs = new TaskCompletionSource();
user.Changed += (s, _) =>
{
try
{
Assert.That(s, Is.EqualTo(user));
tcs.TrySetResult();
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
};
await user.LogOutAsync();
await tcs.Task;
Assert.That(user.State, Is.EqualTo(UserState.Removed));
});
}

[Test]
public void UserChanged_DoesntKeepObjectAlive()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var references = await new Func<Task<WeakReference>>(async () =>
{
var user = await GetUserAsync();
user.Changed += (s, e) => { };
return new WeakReference(user);
})();
await TestHelpers.WaitUntilReferencesAreCollected(10000, references);
});
}

[Test]
public void UserCustomDataChange_RaisesChanged()
{
var tcs = new TaskCompletionSource();
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var user = await GetUserAsync();
user.Changed += OnUserChanged;

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.Android

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [D:\a\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.iOS

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.iOS

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.MacCatalyst

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.MacCatalyst

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, osx-arm64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/private/var/github/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, win-x64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [D:\a\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, osx-x64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, win81

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [C:\run\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, linux-x64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/home/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1040 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Woven classes

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [D:\a\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net7.0]
var collection = user.GetMongoClient("BackingDB").GetDatabase(SyncTestHelpers.RemoteMongoDBName()).GetCollection("users");
var customDataId = ObjectId.GenerateNewId();
var customDataDoc = new BsonDocument
{
["_id"] = ObjectId.GenerateNewId(),
["user_id"] = user.Id,
["age"] = 5
};
await collection.InsertOneAsync(customDataDoc);
var customUserData = await user.RefreshCustomDataAsync();
Assert.That(customUserData!["age"].AsInt32, Is.EqualTo(5));
await tcs.Task;
tcs = new();
// Unsubscribe and verify that it no longer raises user changed
user.Changed -= OnUserChanged;

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.Android

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [D:\a\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.iOS

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.iOS

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.MacCatalyst

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Maui.MacCatalyst

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net6.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, osx-arm64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/private/var/github/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, win-x64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [D:\a\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, osx-x64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/Users/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, win81

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [C:\run\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / net7.0, linux-x64

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [/home/runner/work/realm-dotnet/realm-dotnet/Tests/Realm.Tests/Realm.Tests.csproj::TargetFramework=net7.0]

Check warning on line 1063 in Tests/Realm.Tests/Sync/UserManagementTests.cs

View workflow job for this annotation

GitHub Actions / Test / Woven classes

Nullability of reference types in type of parameter 'sender' of 'void OnUserChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). [D:\a\realm-dotnet\realm-dotnet\Tests\Realm.Tests\Realm.Tests.csproj::TargetFramework=net7.0]
var filter = BsonDocument.Parse(@"{
user_id: { $eq: """ + user.Id + @""" }
}");
var update = BsonDocument.Parse(@"{
$set: {
age: 199
}
}");
await collection.UpdateOneAsync(filter, update);
customUserData = await user.RefreshCustomDataAsync();
Assert.That(customUserData!["age"].AsInt32, Is.EqualTo(199));
await TestHelpers.AssertThrows<TimeoutException>(() => tcs.Task.Timeout(2000));
});

void OnUserChanged(object sender, EventArgs e)
{
tcs!.TrySetResult();
}
}

private class CustomDataDocument
{
[Preserve]
Expand Down
28 changes: 28 additions & 0 deletions wrappers/src/sync_user_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@
#include <realm/object-store/sync/sync_session.hpp>
#include <realm/object-store/sync/app.hpp>
#include "app_cs.hpp"
#include "marshalling.hpp"

using namespace realm;
using namespace realm::binding;
using namespace app;

using SharedSyncUser = std::shared_ptr<SyncUser>;
using SharedSyncSession = std::shared_ptr<SyncSession>;
using UserChangedCallbackT = void(void* managed_user_handle);

std::function<UserChangedCallbackT> s_user_changed_callback;

namespace realm {
namespace binding {
Expand Down Expand Up @@ -86,6 +90,13 @@ void to_json(nlohmann::json& j, const SyncUserIdentity& i)
}

extern "C" {
REALM_EXPORT void realm_syncuser_install_callbacks(UserChangedCallbackT* user_changed_callback)
{
s_user_changed_callback = wrap_managed_callback(user_changed_callback);

realm::binding::s_can_call_managed = true;
}

REALM_EXPORT void realm_syncuser_log_out(SharedSyncUser& user, NativeException::Marshallable& ex)
{
handle_errors(ex, [&] {
Expand Down Expand Up @@ -160,6 +171,23 @@ extern "C" {
});
}

REALM_EXPORT Subscribable<realm::SyncUser>::Token* realm_syncuser_register_changed_callback(SharedSyncUser& user, void* managed_user_handle, NativeException::Marshallable& ex)
{
return handle_errors(ex, [&] {
auto token = user->subscribe([managed_user_handle](const SyncUser&) {
s_user_changed_callback(managed_user_handle);
});
return new Subscribable<realm::SyncUser>::Token(std::move(token));
});
}

REALM_EXPORT void realm_syncuser_unregister_property_changed_callback(SharedSyncUser& user, Subscribable<realm::SyncUser>::Token& token, NativeException::Marshallable& ex)
{
handle_errors(ex, [&] {
user->unsubscribe(token);
});
}

enum class UserProfileField : uint8_t {
name,
email,
Expand Down

0 comments on commit c0fad03

Please sign in to comment.