Skip to content

Commit

Permalink
Initial draft
Browse files Browse the repository at this point in the history
  • Loading branch information
cbullinger committed Aug 7, 2023
1 parent c6d39c8 commit 509f4d2
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
1 change: 1 addition & 0 deletions source/sdk/kotlin/sync.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Device Sync - Kotlin SDK
Handle Sync Errors </sdk/kotlin/sync/handle-sync-errors>
Set the Client Log Level </sdk/kotlin/sync/log-level>
Stream Data to Atlas </sdk/kotlin/sync/stream-data-to-atlas>
Sync Data in the Background </sdk/kotlin/sync/background-sync>
Partition-Based Sync </sdk/kotlin/sync/partition-based-sync>

.. contents:: On this page
Expand Down
162 changes: 162 additions & 0 deletions source/sdk/kotlin/sync/background-sync.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
.. _kotlin-background-sync:

========================================
Sync Data in the Background - Kotlin SDK
========================================

.. contents:: On this page
:local:
:backlinks: none
:depth: 2
:class: singlecol


If you need to sync data when your app isn't running, you can sync realms
in a background process.

Prerequisites
-------------

To get started with background synchronization, you need to add the
following dependencies to your Android application:

- :android:`androidx.work:work-runtime <reference/androidx/work/package-summary>`
to enqueue jobs
- :android:`androidx.concurrent:concurrent-futures <reference/androidx/concurrent/futures/package-summary>`
to return job results from a background worker

Example
-------

Background sync requires two things:

- Synchronization logic
- A scheduled job that periodically performs the sync logic

Synchronization Logic
~~~~~~~~~~~~~~~~~~~~~

First, write the custom logic that synchronizes your realm. Treat this
logic as a standalone connection to your backend. As a result, you'll
need to:

- initialize the Realm SDK
- authenticate a user to open the realm

You can use a user's cached credentials if the user recently used the app.

Open the realm, then use `SyncSession.downloadAllServerChanges() <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb.sync/-sync-session/download-all-server-changes.html>`__
and `SyncSession.uploadAllLocalChanges() <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb.sync/-sync-session/upload-all-local-changes.html>`__
to synchronize the realm fully with the backend. For more information, see
:ref:`Manage Sync Sessions <kotlin-manage-sync-session>`.

You can execute this logic as a background process using a subclass of
:android:`CoroutineWorker <reference/kotlin/androidx/work/CoroutineWorker>`.
Put your synchronization logic in the ``doWork()`` method of your worker:

.. code-block:: kotlin
:caption: Example RealmBackgroundWorker.kt

package com.mongodb.app.worker

import android.annotation.SuppressLint
import android.content.Context
import androidx.concurrent.futures.ResolvableFuture
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.mongodb.app.app
import com.mongodb.app.data.RealmSyncRepository
import io.realm.kotlin.Realm
import io.realm.kotlin.mongodb.syncSession

class RealmBackgroundWorker(context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {

private lateinit var future: ResolvableFuture<Result>
@SuppressLint("RestrictedApi")
override suspend fun doWork(): Result {
future = ResolvableFuture.create()

// Get the realm configuration
val syncRepository = RealmSyncRepository { session, error ->
future.setException(error)
}
val config = syncRepository.getRealmConfiguration()

// Check if user is logged-in
if (app.currentUser?.loggedIn == true) {
val realm = Realm.open(config)
try {
realm.syncSession.downloadAllServerChanges()
realm.syncSession.uploadAllLocalChanges()
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
return future.get()
}
companion object {
const val UNIQUE_WORK_NAME = "RealmBackgroundWorker"
}
}


Worker
~~~~~~

To create a worker that periodically performs background sync:

1. Create a set of :android:`Constraints <reference/androidx/work/Constraints.Builder>`
that specify the conditions required for your worker. Because synchronizing
a realm uses data, you should consider only downloading changes in the
background when the device is *not*:

- low on battery
- using a metered data source

2. Specify how frequently your worker should execute. Your repeat interval
depends on how frequently data updates in the realm and how often users
open your application.

- If the realm frequently updates throughout the day, consider setting a repeat
interval of 1-3 hours.
- If the realm only updates a small number of times each day, it's best to
set a higher repeat interval and only background sync once or twice a day.

3. Enqueue your worker with the Android OS. Assign it a unique identifier
so that you can update the job in the future.

.. tip::

You can create the background sync job inside an Application subclass in
your app to guarantee that the logic only executes once every time your
application runs.


.. code-block:: kotlin
:caption: Example worker

// Create constraints that require a network connection and
// that the device is not low on battery
val constraints: Constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.build()

val backgroundRealmSync =
PeriodicWorkRequestBuilder<RealmBackgroundWorker>(
// Repeat every 12 hours
12, TimeUnit.HOURS,
// Execute job at any point during that 12-hour period
12, TimeUnit.HOURS
)
.setConstraints(constraints)
.build()

// Enqueue the work job, replacing it with the most recent
// version if we update it
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
RealmBackgroundWorker.UNIQUE_WORK_NAME,
ExistingPeriodicWorkPolicy.UPDATE,
backgroundRealmSync
)

0 comments on commit 509f4d2

Please sign in to comment.