Skip to content

Commit

Permalink
Draft
Browse files Browse the repository at this point in the history
  • Loading branch information
cbullinger committed Aug 7, 2023
1 parent 3ee70d3 commit c80ca2f
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 20 deletions.
1 change: 1 addition & 0 deletions source/sdk/kotlin/sync.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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
171 changes: 151 additions & 20 deletions source/sdk/kotlin/sync/background-sync.txt
Original file line number Diff line number Diff line change
@@ -1,31 +1,162 @@
.. _swift-background-sync:
.. _ios-sync-changes-in-the-background:
.. _kotlin-background-sync:

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

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

Sync Changes in the Background
------------------------------

If you want your app to update data in the background (while the app is
minimized), iOS requires you to implement :apple:`Background App Refresh
<documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/updating_your_app_with_background_app_refresh>`.
Enabling Background App Refresh minimizes the time it takes for the user
to see the most recent data; without Background App Refresh, Realm
updates the data when the user launches the app, potentially resulting
in noticeable lag.
If you need to sync data when your app isn't running, you can sync realms
in a background process.

To use the realm while the device is locked, you must adjust the
file protection settings. See
:ref:`use-realm-when-the-device-is-locked`.
Prerequisites
-------------

For an example of how to sync changes in the background using the
SwiftUI ``BackgroundTask`` API, see: :ref:`Sync Data in the Background with
SwiftUI <swiftui-background-sync>`.
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 c80ca2f

Please sign in to comment.