Converting local realm to synced realm in the middle of app life cycle (in Swift)


#1

My app will have a paid feature called multi-devices sync. I would like to implement the feature with Realm Platform - Query Based Sync.

I know how to convert local realm to synced realm thanks to this thread.

But it’s based on the scenario that users sync their realm from the app start - before opening their non-synced local realm. That doesn’t work for me because my users will start sync when they paid for it.

Therefore, I have to convert their local realm in the middle of app life cycle and the local realm is already opened by that time.

My issue starts here. When I try to convert local realm to synced realm, app crashes with this message “Realm at path ‘…’ already opened with different read permissions”.

I tried to find a way to close local realm before converting it, but Realm cocoa does not allow me to close a realm programmatically.

Here’s my code converting local realm to synced realm.

func copyLocalRealmToSyncedRealm(user: RLMSyncUser) {
        
        let localConfig = RLMRealmConfiguration()
        localConfig.fileURL = Realm.Configuration.defaultConfiguration.fileURL
        localConfig.dynamic = true
        localConfig.readOnly = true
        
        // crashes here
        let localRealm = try! RLMRealm(configuration: localConfig)

        let syncConfig = RLMRealmConfiguration()
        syncConfig.syncConfiguration = RLMSyncConfiguration(user: user,
                                                            realmURL: realmURL,
                                                            isPartial: true,
                                                            urlPrefix: nil,
                                                            stopPolicy: .liveIndefinitely,
                                                            enableSSLValidation: true,
                                                            certificatePath: nil)
        syncConfig.customSchema = localRealm.schema
        
        let syncRealm = try! RLMRealm(configuration: syncConfig)
        syncRealm.schema = syncConfig.customSchema!
        try! syncRealm.transaction {
            let objectSchema = syncConfig.customSchema!.objectSchema
            for schema in objectSchema {
                let allObjects = localRealm.allObjects(schema.className)
                for i in 0..<allObjects.count {
                    let object = allObjects[i]
                    RLMCreateObjectInRealmWithValue(syncRealm, schema.className, object, true)
                }
            }
        }
    }

Any help will be appreciated.
Thanks.


#2

I posted this a week ago and it seems that nobody knows any solution for this.
This is a common use case though realm cloud is not ready for this?
Maybe there are fewer apps built with realm cloud than I thought and perhaps I’m the first one who try to sync the realm for the paid users only.


#3

It’s frustrating when you are trying to do a simple thing and you can’t get a response. You can always open a support ticket but before doing that, a few things to look at.

First, and not to rehash old topics but read Does anyone user Realm Cloud for production. The last note by @roberhofer is very important.

Second thing is the Github article you are following is two years old and is not current. The code isn’t correct. Realm has changed and updated just about all aspects of syncing in the last couple of years. We spent literally months trying to get a query based sync to work and never could - mostly due to incorrect and outdated documentation.

It’s better now so take a look at the current docs Using Sync’d Realms

Here’s a question; if you’re currently using a local realm, why are you doing this

let localRealm = try! RLMRealm(configuration: localConfig)

Can’t you just do this to get the default local realm?

// Get the default Realm
let realm = try! Realm()

Also… If you want to open a query based realm the code is something like this (from the current docs)

// Create the configuration
let syncServerURL = URL(string: "realms://myinstance.cloud.realm.io/~/userRealm")!
let config = user.configuration(realmURL: syncServerURL)

// Open the remote Realm
let realm = try! Realm(configuration: config)
// Any changes made to this Realm will be synced across all devices!

Lastly, I believe you need to be using SyncUser.configuration() for accessing the sync’d realm. Here’s a link to the SyncUser API


#4

Thank you for your reply.

The reason why I am trying to open a new local realm of type RLMRealm is Realm() and RLMRealm() has different properties.

If I use a default Realm which is already opened, I can’t access allObjects properties thus I can’t copy local realm to synced realm.

let allObjects = localRealm.allObjects(schema.className)

Also, Realm and RLMRealm both have schema property but they are different type so I can’t assign Realm.schema to RLMRealm.schema.

syncConfig.customSchema = localRealm.schema

I wish to know if there is a way to copy local realm to synced realm without creating a new RLMRealm().

PS. Here’s another one who suffered from the same issue. And it’s not clear that he somehow solved the issue. It seems that there’s no official solution for this issue.


#5

The use case here is kind of unclear. Why would you want to manually copy your local data to the sync server? That defeats the purpose of having a sync server as it does that for you automatically.

From the docs

When you open a realm on a device, the objects it contain will be replicated from the server to the device or vice versa, so that you have them available locally which means that you always have zero-latency access to them and that the app will keep working even if it goes online. Any changes done to the objects in the realm will eventually be reflected in the all other instances of the same realm on other devices.

If you’re using a sync’d realm you select what objects sync to the server and for those that do not, are only housed locally. In other words if you sync a subset of your data, then only that data is moved to the server. You can optionally sync all objects. Additionally you can have a query based sync or a full sync.

When opening the realm, you can choose to only replicate out a subset of the objects specified with a set of queries (Query Based Sync), or you can use full sync which circumvents the overhead of doing queries and just replicates the entire realm (Full Sync).

So, if you can have the data moved automatically why don’t you not sync the data, so it stays local, until you’re ready and then just turn on sync’ing which will then do what you asked:

copy local realm to synced realm