In this part of our learning path, you will walk through the "Audit Inventory" demo application of using Couchbase Lite with the Replicator, which means the database will sync information using two way replication between the mobile app and a Couchbase Server using Sync Gateway.
In previous steps of the learning path we have enabled Live Queries on both the Project Listing screen and the Audit Listing screen. Once replication is started those screens will update anytime new documents or changes to existing documents are replicated to the mobile device.
NOTE: This step assumes you completed the all previous steps of the learning path and specifically the
Sync Gateway Setup
that sets up Sync Gateway and Couchbase Server for this demo in docker on your local computer. You MUST complete this part of the learning path before moving forward.
In this step of the learning path you will learn the fundamentals of:
While the demo app has a lot of functionality, this step will walk you through:
Learn Couchbase Lite with Kotlin and Jetpack Compose
repository from GitHub.git clone https://github.com/couchbase-examples/android-kotlin-cbl-learning-path.git
Developers need to decide when to start replication. For some apps it makes sense after the user logs in, while for others it might make more sense once the application is fully loaded. In this app, two-way Replication between the app and the Sync Gateway is enabled from the Replication screen manually. Replication can't be started until the configuration is filled out, which is handled by another screen.
Log in to the app with any username and password. Let's use the values "demo@example.com" and "P@ssw0rd12" for username and password fields respectively.
In the Demo App the Replicator screen can be found by tapping on the Drawer menu icon (sometimes referred to the Hamburger icon) in the upper left hand corner of the screen and tap on the Replication link.
ws://10.0.2.2/projects
which is the IP address used by emulators to talk to your local computer. If you are running this on a physical device, you will need to change this IP to your local computers IP.Sync Mode
and defaults to Push and PUllSync Mode
settings and defaults to on which means the application will contiue to replicate until manually stopped.NOTE: At this point your Couchbase Server and Sync Gateway servers should be started in Docker which was covered in the
Sync Gateway Setup
step of the learning path. If you start replication and the Sync Gateway Server is not started, you will time out trying to connect to it.
Valdiating that the documents replicated from the device and from server can be done from the mobile app and from Couchbase Server since we have two-way replication enabled.
In the Demo App navigate to the Home screen which can be found by tapping on the Drawer menu icon (sometimes referred to the Hamburger icon) in the upper left hand corner of the screen and tap on the Home link.
Scrolling through the list you should see three new documents starting with the word Warehouse
and then a number. The numbers are different for each group of teams.
demo2@example.com
or demo4@example.com
with the same password and starting the replicator to see those documents listed above.Open your web browser of choice and navigate to the following URL: http://localhost:8091/
Log into the portal using the same username and password that was displayed in the top of the Couchbase Server docker logs:
Username: Administrator
Password: P@$w0rd12
From the Cluster > Dashboard page click on Buckets link on the navigation menu on the left side of the screen.
On this screen you can see the number items in the buck should have increased from the Sync Gateway Setup step of the learning path. To view the documents, click on the Documents link to open the Document browser.
Change the Limit box from the default value to 50 and click the Retrieve Docs button.
Scrolling through the list you should find your random documents that were created on the mobile device during the Batch operations step of the learning path.
Now that we have tried out replication, let's review how the replicator and replicator configuration code is setup. All code used by the ReplicatorViewModel and ReplicatorConfigViewModel us located in the ReplicatorServiceDb class.
Open the ReplicatorServiceDb.kt file.
The override of replicatorConfig is used to setup the config with default values that you saw on the Replication Configuration screen.
//if your sync gateway server is running on a different IP change it here
override var replicationConfig = mutableStateOf(
ReplicatorConfig(
username = loggedInUser.username,
password = loggedInUser.password,
endpointUrl = "ws://10.0.2.2:4984/projects",
replicatorType = "PUSH AND PULL",
heartBeat = 60L,
continuous = true,
selfSignedCert = true
)
)
As stated in the comments if your Sync Gateway server is not running on your local computer, this is where you would change that setting.
Anytime the replication configuration is changed on this screen the updateReplicatorConfig function is called.
replicatorManager?.let { replicatorResources ->
val urlEndPoint = URLEndpoint(URI(replicationConfig.endpointUrl)) // 1
replicatorResources.replicatorConfiguration = ReplicatorConfiguration(replicatorResources.database, urlEndPoint) // 2
replicatorResources.replicatorConfiguration?.let { replicatorConfiguration -> //3
replicatorConfiguration.isContinuous = replicationConfig.continuous // 4
when (replicationConfig.replicatorType) { // 5
"PULL" -> replicatorConfiguration.type = ReplicatorType.PULL // 5
"PUSH" -> replicatorConfiguration.type = ReplicatorType.PUSH // 5
else -> replicatorConfiguration.type = ReplicatorType.PUSH_AND_PULL // 5
}
val authenticator = BasicAuthenticator( // 6
replicationConfig.username, // 6
replicationConfig.password.toCharArray() // 6
)
replicatorConfiguration.setAuthenticator(authenticator) //6
replicatorResources.replicator =
Replicator(replicatorManager?.replicatorConfiguration!!) //7
}
canStartReplication.value = true //8
this.replicationConfig.value = replicationConfig //9
}
override fun startReplication() {
try {
replicatorManager?.replicator?.start()
isReplicationStarted = true
} catch (e: Exception){
Log.e(e.message, e.stackTraceToString())
}
}
override fun stopReplication() {
try {
replicatorManager?.replicator?.stop()
isReplicationStarted = false
canStartReplication.value = false
} catch (e: Exception){
Log.e(e.message, e.stackTraceToString())
}
}
The stop function is called on the replicatorManager's replicator
The canStartReplication variable is used to force the user to go back into the configuration screen and click save again before starting replicaton. This is because when the application is sent to the background this method is called so that replication doesn't run in the background and try to update UI components that it doesn't have access to because they are no longer running on the main thread.
val closeDatabase: () -> Unit = {
viewModelScope.launch(Dispatchers.IO) {
context.get()?.let {
replicatorService.stopReplication()
DatabaseManager.getInstance(it).closeDatabases()
}
}
}
@Composable
fun MainView(startDatabase: () -> Unit,
closeDatabase: () -> Unit) {
// Safely update the current lambdas when a new one is provided
val currentOnStart by rememberUpdatedState(startDatabase)
val currentOnStop by rememberUpdatedState(closeDatabase)
//if lifecycleOwner changes, dispose and reset the effect
DisposableEffect(lifecycleOwner) {
// Create an observer that triggers our remembered callbacks
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
currentOnStart()
} else if (event == Lifecycle.Event.ON_PAUSE) {
currentOnStop()
}
}
// Add the observer to the lifecycle
lifecycleOwner.lifecycle.addObserver(observer)
// When the effect leaves the Composition, remove the observer
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
Lifecycle.Event.ON_START
is used to call the startDatabase function in our MainViewModelLifecycle.Event.ON_PAUSE
is used to call the closeDatabase function in our MainViewModel.In this exercise, we will observe how changes made on one app are synced across to the other app
Data conflicts are inevitable in an environment where you can potentially have multiple writes updating the same data concurrently. Couchbase Mobile supports Automated Conflict Resolution. You can learn more about this in the documentation on Handling Data Conflicts.
Congratulations on completing this step of the learning path! In this section, we walked through setting up the Replicator Config and started two-way replication between a mobile app and Sync Gateway.