7

I'm creating an iOS application, where I intend to provide data sync across device feature, only to the premium users. I find Realm Sync as a good solution to keep the local on-device database and cloud MongoDB Atlas in sync. However, I don't want to sync the data of the non-premium users to the cloud database.

I'm enlisting a couple of ways that I can think of to prevent Realm Sync from triggering for non-premium users, but I'm not sure on what is the best way for this problem.

  • Prevent syncing by leveraging Sync permissions - I can store list of premium user ids and only give sync permissions to those users.

     {
       "%%user.id": [
          "5f4863e4d49bd2191ff1e623",
          "5f48640dd49bd2191ff1e624",
          "5f486417d49bd2191ff1e625"
       ]
     } 
    
  • Configure Realm objects on client side i.e. only allow all Realm objects / models if the user is premium.

     // Get a configuration to open the synced realm.
     var configuration = user.configuration(partitionValue: "user=\(user.id)")
     // For non-premium user it would be [User.self]
     configuration.objectTypes = [User.self, Project.self] 
     Realm.asyncOpen(configuration: configuration) { [weak self](result) in /*...*/ }
    

I'm looking for insights / recommended approach to this problem.

Edit

I've a few additional questions about handling two use cases differently - non-premium one by opening a local only Realm() and the premium one with Realm.asyncOpen().

  1. How to handle a use case when an existing user switches to a premium subscription? Should calling Realm.asyncOpen() suffice or do I need to do any special handling?
  2. I plan to sync all my User (custom document in a collection) records for all users (premium + non-premium). My guess is I should open a normal Realm for all my conent and synced Realm with just [User.self] object in the configuration.
Siddharth Kamaria
  • 2,448
  • 2
  • 17
  • 37

1 Answers1

8

This is super easy to do!

When you only want to work with a local realm, connect to it with no config - like this

let realm = try! Realm()
let someObject = realm.results(SomeObject.self)

or a config that maybe contains a local file name. All of the app data will only be read and written locally with no sync'ing.

When you want to use MongoDB Realm Sync, connect to it like this

let app = App(id: YOUR_REALM_APP_ID)
// Log in...
let user = app.currentUser
let partitionValue = "some partition value"
var configuration = user!.configuration(partitionValue: partitionValue)
Realm.asyncOpen(configuration: configuration) { result in
    switch result {
    case .failure(let error):
        print("Failed to open realm: \(error.localizedDescription)")
        // handle error
    case .success(let realm):
        print("Successfully opened realm: \(realm)")
        // Use realm
    }
}

and then later with a config

let config = user?.configuration(partitionValue: "some partition")
let realm = try! Realm(configuration: config)

EDIT

Answering the two followup question:

How to handle a use case when an existing user switches to a premium subscription? Should calling Realm.asyncOpen() suffice or do I need to do any special handling?

Connecting to MongoDB Realm with the Sync'ding solution will add additional files and start syncing. If this is a new user that's 'premium', theres nothing else to do, other than (initially) ensure your objects are correctly structured with _id and partitionKey properties.

If this user is upgrading from a non-premium local only to a premium that's sync'd you will need to copy your realm objects from the local only realm to a sync'd realm.

There are several ways to to that; probably the simplist is to include code in your app then when upgrading, connects to a sync realm (using .async), then connects to your existing local realm and finally iterate over the objects to copy to the sync'd realm.

Another option is to export the the realm objects as JSON and then write them to the server directly. The next time your app connects with .async, it will force a client reset and download and create the locally sync'd files. There are some tidbits of information that may help with this particular process in the Realm Legacy Migration Guide

I plan to sync all my User (custom document in a collection) records for all users (premium + non-premium). My guess is I should open a normal Realm for all my conent and synced Realm with just [User.self] object in the configuration.

Non-premium users don't sync so they are not really 'users' as such. You wouldn't need to store them or sync them so you really don't need any authentication or store any data on the server - it's just a locally run and used app so there isn't even a 'user' object to worry about. You will need to do that once they upgrade.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • This is really helpful for me since I'm just getting started with Realm. I've a few follow up questions which I'll add to the question. I'd love to get some insights from your end :) – Siddharth Kamaria Mar 10 '21 at 18:33
  • @SiddharthKamaria Glad to help! Try to keep questions limited to one topic/question - when a single question has more than one topic, it makes answers very long and hard to follow. If this answer helped, accept it so it can help others [How To Accept](https://stackoverflow.com/help/someone-answers) and then create a different question for your other topics and we'll take a look! – Jay Mar 10 '21 at 18:36
  • I'll definitely try this out and let you know. If you can, please answer the two questions that I had either in the answer or in the comments. :) – Siddharth Kamaria Mar 10 '21 at 18:39
  • @SiddharthKamaria I updated my answer with additional info. – Jay Mar 10 '21 at 19:05
  • Thank you so much! As far as I understand for que 1., having a nullable partition key should suffice and when a user switches from free to premium user, I should populate the partition key and open a synced Realm. Correct me if I've misunderstood. – Siddharth Kamaria Mar 11 '21 at 06:34
  • Or for that matter, I can populate the partition key and just open a local realm! That should also work - as you've rightly mentioned in the answer. – Siddharth Kamaria Mar 11 '21 at 06:38
  • 1
    @SiddharthKamaria *I can populate the partition key and just open a local realm!* that is correct. Ensure you include _id and a partition key in your objects in every case so if the user upgrades from local to sync, it will be a smooth transition. – Jay Mar 11 '21 at 16:48
  • Have you tried this out? My understand was it was not possible to sync a locally created realm. I've attempted this solution and two different realm files were created for the different use cases (local and synced). Was I missing something? – David Mar 23 '21 at 18:50
  • @David *All* realms are locally created; the essence of Realm is local first (sync second). It's just that a sync'd realm has a different file structure on disk than a local-only realm. If you create an app that is only sync'd, Realm doesn't create different realm files for different use cases - other than Realm Sync'd files are separated by partition. If you have a specific issue, post a question and we'll take a look! – Jay Mar 23 '21 at 18:55
  • There is a small factual error in the answer which @David was also trying to convey. Converting a local realm to sync'd requires you to copy all objects from the local realm to a new sync'd realm. I also came across a question on MongoDB Forums about the same and Ian Ward from Realm team did mention copying all objects as the solution. Unfortunately, I am unable to find that question right now. – Siddharth Kamaria Mar 27 '21 at 07:41
  • @SiddharthKamaria That's correct and thank you for the clarification. The objects need to be copied; one option Realm suggests exporting to JSON and then uploading them to Realm Atlas via a script or an app. Then when you main app connects via async, it will automatically perform a sync and download the data, creating the appropriate file structures on disk. There is some good info in [Migration](https://docs.realm.io/realm-legacy-migration-guide/) and the post you mentioned from Ian is [here](https://developer.mongodb.com/community/forums/t/migrating-non-sync-data-to-a-synced-realm/10158/2) – Jay Mar 27 '21 at 14:04
  • 1
    @SiddharthKamaria ...and thanks for pointing that out. My answer was a little unclear on that point so I updated it with more info. – Jay Mar 27 '21 at 14:13
  • @Jay Thank you for linking that post and providing an alternate solution. These efforts do help new developers get started with Realm! – Siddharth Kamaria Mar 27 '21 at 18:10