Pattern or Technique to sync local Realm (Xamarin) with Cloud Realm

cloud

#1

Hello!

This is more of an architectural question than anything else. I’m planning a solution that will have an admin MVC web site where I will periodically update Realm data in a cloud instance, and a Xamarin Forms application (iOS and Android) that will only read data. I’d like to provide offline capabilities, so I’m thinking having a local Realm as part of the application that syncs data from the Cloud instance when the mobile application is opened is the way to go. That’s only my initial thought, however. I just discovered how to set up a connection to the sync’d Realm and manipulate data from the MVC site. I can also connect to the Cloud instance through a Xamarin app.

What I’m looking for is advice, patterns and/or suggestions as to 1) if this is the correct way to think about this, or there’s another way, and 2) any example projects or code that could help in the instigation of this plan.

Thank you in advance.


#2
  1. Yep - that’s pretty much what Realm does :slight_smile:

  2. You can check out the Realm Tasks demo app for a very basic demo on how synchronized Realms work. If you’re not concerned about reading some Swift/Java, you can also take a look at our more advanced tutorials that cover some of the newer features, such as partial sync and fine-grained permissions. There’s also an article I wrote that explores some basic concepts of Realm, such as data-binding and change notifications.

And of course, if you have some more specific questions, I would be happy to take a look :slight_smile:


#3

@nirinchev - Thank you for that. I do have one question regarding permissions though. I created the realm from my MVC app with an Admin user I set up in Realm Studio that has the Admin role. The realm is /vb. I’d like to add another user (VBUser) to the realm and have that user have read only permissions. I’ve been through the documentation and can’t find a way in code to grant these permissions to this user. Any insight?

Thank you!


#4

You can take a look at the Access Control docs. These should have the examples you’re looking for.


#5

@nirinchev - I looked at those docs and every I run the code to add a user to the realm and try to give permissions, I get the error “KeyNotFoundException: The given key was not present in the dictionary.”. This is the code:

realm.Write(() =>
            {
                var readOnlyRole = PermissionRole.Get(realm, "read-only"); // This is where the error is thrown
                readOnlyRole.Users.Add(roUser); // Readonly user created with LoginAsync

                var permission = Permission.Get(readOnlyRole, realm);
                permission.CanRead = true;
            });

My understanding is that it’s supposed to create a permission role and allow me to add the user to that role. Then, I can set the permissions for that role. Is this true?


#6

Can you post the code you use to open the Realm? In particular, this code will only work for partial Realms, so make sure to set IsPartial = true in the configuration.


#7

Sure thing:

            var credentials = Credentials.UsernamePassword("{username}", "{password}", false); // Admin user

            var authUri = new Uri(AuthUrl); // "https://vbseasons.us1.cloud.realm.io/"
            var user = await Realms.Sync.User.LoginAsync(credentials, authUri);

            var serverUrl = new Uri(RealmUrl); // "realms://vbseasons.us1.cloud.realm.io/vb"
            var conf = new SyncConfiguration(user, serverUrl);

            var realm = Realm.GetInstance(conf);

#8

Yep, as I suspected, you need

var conf = new SyncConfiguration(user, serverUrl)
{
    IsPartial = true
};

You can read more on Partial Sync in the docs. Just a heads up though - once you open a Realm as non-partial (or “full”), you can’t open it as partial after that, so your vb Realm is now a “full” Realm, and the fine-grained permissions are not something you can apply to it, but you can still work with path-level permissions if that fits your use case.


#9

@nirinchev - Ok this is getting annoying. Seems this should be much easier than it is. I’m sure my /vb realm is a full realm and I’m trying to give a regular user called VBUser read access to the full realm. According to the Path-Level Permissions section, I should be able to modify the permissions and give this user read permissions by running this code:

           var credentials = Credentials.UsernamePassword("{username}", "{password}", false);

           var authUri = new Uri(AuthUrl);
           var user = await Realms.Sync.User.LoginAsync(credentials, authUri);

           var condition = PermissionCondition.UserId("ecd7b4d8c9a5e29de0ae29052d5287f8");
           await user.ApplyPermissionsAsync(condition, RealmUrl, AccessLevel.Read); // "realms://vbseasons.us1.cloud.realm.io/vb" - This is the line that hangs - no response at all after this is called.

The await user.ApplyPermissionsAsync call hangs and doesn’t resolve.


#10

Are you performing this operation on a thread that has a synchronization context (e.g. the main thread)? If not, then that’s the problem. Also, based on your snippet, it’s not clear if you’re catching any exceptions, are you sure the call never resolves rather than throw an error?


#11

@nirinchev - I’m running this on the Index.cshtml.cs page of my MVC app, in the async OnGet() method. I also tried running this in my Xamarin app in the initial page xaml.cs async OnAppearing() method and it still just hung there. I applied a try/catch block to the line hanging and no error occurred.


#12

@nirinchev

I have a similar problem. I’m using an admin user in an .net core API controller action, and I’m trying to add write permissions to a realm using:

var condition = PermissionCondition.UserId(user.Identity);
AsyncContext.Run(async () =>
{
await AdminUser.ApplyPermissionsAsync(condition, RealmUrl(agreementNo), AccessLevel.Write);
});

but the process never recovers from the AdminUser.ApplyPermissionsAsync call. It is just hanging somewhere.

The server log says:

HTTP response: a9fd1a19-f524-4b66-a727-f1477b153c34 {“type”:“https://realm.io/docs/object-server/problems/invalid-credentials",“title”:"The provided credentials are invalid or the user does not exist.”,“status”:401,“code”:611}

I am creating realm with IsPartial = false, in the root, using admin user.
If I simply skip the ApplyPermissionsAsync everything else works - and that includes a pretty big sync that populates the realm (using the AdminUser).
The user in “user.Identity” get created ok, just before the call to ApplyPermissionsAsync. I can see it in the realm cloud browser.

Any ideas?


#13

Can you try to put the login logic of the admin user inside the AsyncContext.Run block? So it’s something like:

AsyncContext.Run(async () => {
    var adminUser = await User.LoginAsync(...);
    await adminUser.ApplyPermissionsAsync(...);
});

#14

@nirinchev
Putting it all in the same AsyncContext does remove the error from the log.
But the process still hangs on the AdminUser.ApplyPermissionsAsync call.
I’ve sent the instance url as a private message to you, if you fancy looking at the logs in more detail.


#15

@nirinchev
This issue looks a bit similar: https://github.com/realm/realm-dotnet/issues/1734


#16

Hm… you’re right. You can verify that by setting a Session.Error handler and logging any exceptions that bubble up there.


#17

@nirinchev
No errors bubble up in Session error logging.
ApplyPermission just gets stuck, and bring everything to a halt.
If I skip ApplyPermission, everything works like a charm.


#18

Hm… odd, I would have expected an SSL validation error to be reported there :confused: The fix for the SSL issue is almost ready - I’ll ping you when we have a prerelease package (hopefully early next week).


#19

@nirinchev

It’s unfortunate indeed. I’m in a pickle here.

I’ve tried to code around the issue by letting the user create the database (thus being “born” with the proper permissions), but then I get the problem of not being able to control the filename of the database.

Can I use the optionalPath of SyncConfiguration to somehow enforce my own naming scheme and avoid the nonsensical userid in the database file name?


#20

Non-admin users can’t write to a path that doesn’t start with their user Id. But if it’s a one-time setup, you could perform it on iOS device or a .NET Core app running on macOS - these shouldn’t suffer from the Android/Windows SSL issues.