Realm offers a nice technique to share Realms with other users using the PermissionOffer and the PermissionOfferResponse objects. The idea is that you can provide a token that an other user can use to connect to your data store. In this blogpost we will use this flow in combination with a QR code -generated by ZXing- to let an other user connect to a data store and start a chat (or anything).

The problem with demos

I’ve been playing with Realm for some time now. With this platform it is as easy as it can get for developers to create apps that can do real-time data synchronization. Sometimes it does feel a bit like magic… And so I created some stuff with it, typical demo-stuff. And most of the times, these demos do something like this:
  • connect to a Realm with UserA
  • display an object from that Realm on the screen
  • take a second device
  • connect to the same Realm with UserA
  • display the same object from that Realm on the screen
  • make changes to the object being displayed on your first device
  • see how the value almost instantly updates on the second device as well
And that, on it’s own is already an incredibly cool demo!! Also due to the fact that it involves such little code.
But there is a small caveat in the scenario above… On both devices, I’m logged in as the same user. What if I want to use different users? We can of course, when we create our Realm, define that UserB and UserC also have access to that Realm. But that is all too easy and not worthy of an entire blogpost. 🙂 I the scenario I was developing, I wanted to ‘invite’ anybody that I would want to connect to my Realm, without actually knowing what his or her username is on my Realm Object Server. And I got just the right tool for the job:

PermissionOffer and PermissionOfferResponse

When taking a look at the Realm documentation, we see that there is the ability to create a PermissionOffer. (https://realm.io/docs/xamarin/latest/#permissionofferpermissionresponse) In this PermissionOffer object we can define which permissions we want to offer on our Realm (who would have thought that…). We don’t define a particular user, because the purpose of this PermissionOffer is that whoever gets and accepts the offer gets the provided permission on our data store. When we create a PermissionOffer, we get back a Token (a string) from Realm. This token can then be processed by the other user by passing it in as a parameter when newing up a PermissionOfferResponse. Look at it like some kind of handshaking that needs to be done. Once this PermissionOfferResponse  is processed, we get back all the information we need to connect to that Realm.
The final piece of the puzzle is getting that token to an other user. For this I just used the ZXing library to generate a QR code from that token. The other user can then easily from within our app scan the code so that we get the token and do our little handshaking.

Show me the code!

The inviting side of the story

To create an invite, I’ve built a InivitaionHelper class that has a static method CreateInviteAsync.

public static Task<MyInvitation> CreateInviteAsync(string realmUrl)
{
    var tcs = new TaskCompletionSource<MyInvitation>();

    var permissionOffer = new PermissionOffer(realmUrl,
                                 mayRead: true,
                                 mayWrite: true,
                                 mayManage: false,
                                 expiresAt: DateTimeOffset.UtcNow.AddDays(7));

    var managementRealm = User.Current.GetManagementRealm();

    managementRealm.Write(() =>
    {
        managementRealm.Add(permissionOffer);
    });

    permissionOffer.WhenPropertyChanged(nameof(PermissionOffer.Status),
        (po) => po.Status == ManagementObjectStatus.Success,
        (po) =>
        {
            tcs.TrySetResult(CreateInvite(po));
        });

    return tcs.Task;
}

private static MyInvitation CreateInvite(PermissionOffer po)
{
    return new MyInvitation(po.Token);
}

This method will basically do all the boilerplate code for generating the PermissionOffer and eventually will return us a MyInvitation object. This object is just a wrapper around the Token that has been generated by Realm. I’ve made this wrapper class so I could add some meta data to the invitation if I wanted.
When the PermissionOffer object is written to the Management Realm of the current user, Realm will do its thing in the background and will update the PermissionOffer object once it’s done: the status of the object will be set to ‘Success’ and the Token property will be filled in. Fortunately, almost all Realm objects implement INotifyPropertyChanged, so we can determine exactly when Realm has processed everything and thus has updated the values on the PermissionOffer object.
For these kinds of things, I’ve written an extension method WhenPropertyChanged for objects that implement the INotifyPropertyChanged  interface:

public static void WhenPropertyChanged<T>(this T obj, string property,
    Predicate<T> predicate, Action<T> action) where T : INotifyPropertyChanged
{
    obj.PropertyChanged += (sender, e) =>
    {
        if (e.PropertyName == property && predicate((T)sender))
            action((T)sender);
    };
}

The action (last parameter) gets executed when the provided property changed and the given predicated returns true. With this extension method, I can easily write the following:

permissionOffer.WhenPropertyChanged(nameof(PermissionOffer.Status),
    (po) => po.Status == ManagementObjectStatus.Success,
    (po) =>
    {
        tcs.TrySetResult(CreateInvite(po));
    }
);

So WHEN the Status property value changes, AND the value of the Status property is ‘Success‘, we can new up an instance of MyInvitation from the token of the PermissionOffer object. I Set the result of my TaskCompletionSource to this newly created MyInvitation object. This way, I can elegantly call this method like this:

MyInvitation invite = await InvitationHelper.CreateInviteAsync(ApplicationConfig.REALM_URL);

I just love it when I can abstract code away so elegantly like this, don’t you?

Finally, on the inviting side of the story, the only thing left to do, is generating a QR code. The QR code will be our MyInvitation object serialized as a Json string.
To display a QR code, I’ve used the ZXing libary. This library makes it a breeze to write and read QR codes (and others). Just install it through NuGet (ZXing.Net.Mobile.Forms) and you’re good to go.
On my InvitePage, I just put a ZXingBarcodeImageView control:

<ZXing:ZXingBarcodeImageView 
                    x:Name="BarCodeView"
                    HorizontalOptions="Fill"
                    VerticalOptions="Fill"
                    HeightRequest="300"
                    WidthRequest="300"
                    Margin="10"
                    BarcodeFormat="QR_CODE" />

In the code-behind of my Invite page, I’ve got a constructor that takes a MyInvitation object. This parameter gets serialized by using Newtonsoft.Json and this serialized content is set as the BarcodeValue of my ZXingBarcodeImageView control.

public InvitePage(MyInvitation invite) 
    : this(Newtonsoft.Json.JsonConvert.SerializeObject(invite))
{ }

public InvitePage(string content)
{
    InitializeComponent();
    BarCodeView.BarcodeOptions.Height = 300;
    BarCodeView.BarcodeOptions.Width = 300;
    BarCodeView.BarcodeValue = content;
}

The invitee side of the story

On this side of the story, need to scan the QR code generated by the other user, parse it (we know it is an object of type MyInvitation), take the token (and maybe som additional meta data), create a PermissionOfferResponse passing in the token and write this object to the user’s Management Realm.
First things first: we need to create a new ZXing.Mobile.MobileBarcodeScanner. Once we have this, we can call the Scan method which will return the string value of the scanned QR/bar code.
We can then deserialize this string to a MyInvitation object.

public static async Task<MyInvitation> ScanInviteAsync()
{
    var scanner = new MobileBarcodeScanner();

    var result = await scanner.Scan();

    if (result != null)
    {
        try
        {
            return JsonConvert.DeserializeObject<MyInvitation>(result.Text);
        }
        catch (Exception)
        {
            //ToDo
        }
    }
    return null;

In the static InvitationHelper  class that I used earlier, I’ve also added an AcceptInvitationAsync method. Analogous to the CreateInviteAsync method, this method does all the boilerplate stuff to accept the PermissionOffer. From my MyInvitation object, I can get the Token from the PermissionOffer and new up a PermissionOfferResponse object which I can then save to the user’s ManagementRealm. Once I’ve saved this, again Realm will do its thing and so the handshaking is done.

public static Task<Realm> AcceptInvitationAsync(MyInvitation invitation)
{
    var tcs = new TaskCompletionSource<Realm>();

    var offerResponse = new PermissionOfferResponse(invitation.Token);
    var managementRealm = User.Current.GetManagementRealm();

    managementRealm.Write(() =>
    {
        managementRealm.Add(offerResponse);
    });

    offerResponse.WhenPropertyChanged(
        nameof(PermissionOfferResponse.Status),
        (por) => por.Status == ManagementObjectStatus.Success,
        (por) =>
        {
            if (por.RealmUrl != null)
            {
                var configuration = new SyncConfiguration(User.Current, new Uri(por.RealmUrl));
                tcs.TrySetResult(Realm.GetInstance(configuration));
            }
        });

    return tcs.Task;
}

Here I use the same WhenPropertyChanged extension method that I’ve used earlier to listen for the PermissionOfferResponse object’s Status property to be set to Success. Once that’s done, I can create a SyncConfiguration object by using the RealmUrl  I got from the PermissionOfferResponse. With this SyncConfiguration  object I can create an instance of a Realm object. The resulting Realm object will point to the Realm data store userA shared.
Both UserA and UserB can now read from and write to this Realm data store. On both clients the data is kept in sync in real-time!

Demo

I’ve made a small chat app to demonstrate this technique of sharing a realm by using a QR code. You can find it on my GitHub: https://github.com/PieEatingNinjas/SimpleRealmChat

 

NOTE
When adding the XZing library to you app, make sure you do the following:

On Android, before LoadApplication:
ZXing.Net.Mobile.Forms.Android.Platform.Init();
MobileBarcodeScanner.Initialize(Application);

On iOS, before LoadApplication:
ZXing.Net.Mobile.Forms.iOS.Platform.Init();
and add the following to the Info.plist:
<key>NSCameraUsageDescription</key>
<string>The app would like to use your camera</string>