Add Collection-Level Watch¶
Author: MongoDB Documentation Team
This tutorial walks you through adding collection-level Watch to your To-do client(s). With this, your client will be automatically notified when a to-do item is changed in the Atlas database.
Time required: 30 minutes
What You’ll Need¶
- If you have not yet built the To-do backend, follow the steps in this guide.
- You can either follow this tutorial to build the client app, or start with an app that already has most of structural work done for you.
Procedure¶
- Android (Java)
- iOS (Swift)
- Web (JavaScript/React)
A. Update your client
For the client work in this tutorial, you can start with the app you built in the previous tutorial or start with an app that already has some of the Android structural work done for you.
Get the latest code¶
In the MongoDB Stitch Android tutorial repo, run the following command to fetch the tagged version for this tutorial:
Update strings.xml¶
You need to make sure the client app points to your Stitch backend
app. To do so, follow these steps to add your Stitch app ID to the
strings.xml
file:
- Open the
res/values/strings.xml
file. - Set the value of the
stitch_client_app_id
key to your Stitch App ID.
Add collection-level Watch for all items¶
We will now add the code to set up a collection-wide watch on the items
collection. This initial code will notify your client app every time any
item is changed; later on, we’ll change the code to only notify your app
if the item belongs to the logged-in user.
Open the TodoListActivity.java
file and follow these steps:
Create a method called
addWatchToCollection
that takes no parameters and returns void. If you are using the sample app, we have created the method stub for you, which looks like the following:We’ll now add code that sets up a filtered collection-level watch for documents in the items collection, so that your client app will be notified whenever a change occurs to one of your items in the collection. On the
items
collection, call the watchWithFilter method, passing in a new BSON document that limits the search to only documents where theowner_id
field matches the userId` value. Then calladdOnCompleteListener
, passing in a new onComplete method. YouraddWatchToCollection
method may look like the following:When you first set up your Stitch backend app, you configured a rule that prevents a user from reading from, or writing to, another user’s items. Because of this rule, you don’t actually need to use
watchWithFilter()
, as a call to watch accomplishes the same thing:In our example app, we’ve extracted the onCompleteListener to its own method for clarity. The method signature looks like the following:
In the
addOnCompleteListener
add your business logic for how you want to handle change notifications. In this tutorial, we’ll do two things:- Pop up a Toast to notify the user, and
- Refresh the list of to-do items to display the changes.
To do these two things, add the following code to the onCompleteListener:
Note
The use of the runOnUiThread method is required to show a Toast from a non-UI thread.
Finally, call the
addWatchToCollection
method from thedoLogin
method, after verifying that we have an authenticated user and setting the globaluserId
parameter. Find thedoLogin
method, and add the call toaddWatchToColleciton
. When complete, yourdoLogin
method should look like the following:
A. Update your client
For the client work in this tutorial, you can start with the app you built in the previous tutorial or start with an app that already has some of the iOS structural work done for you.
Get the latest code¶
In the MongoDB Stitch iOS tutorial repo, run the following command to fetch the tagged version for this tutorial:
Watch the items collection for changes¶
We will now add the code to set up a watch on the items
collection.
This code will notify your client app every time an item is added, updated,
or deleted.
Open the TodoTableViewController.swift
file and follow these steps:
Create a method called
addWatchToCollection()
:Make the
TodoTableViewController
conform to theChangeStreamDelegate
protocol so that it can receive watch events. First, at the top of the file, importStitchRemoteMongoDBService
to makeChangeStreamDelegate
available:Declare adoption of the
ChangeStreamDelegate
protocol onTodoTableViewController
by addingChangeStreamDelegate
to the end of the adopted protocols list:This protocol relies on a type declaration in your class to determine the document type used by your collection. The type declaration must be called
DocumentT
. In this case, theitemsCollection
uses ourTodoItem
struct, so ourDocumentT
must be an alias forTodoItem
. Put the type alias at the top of your class that implements theChangeStreamDelegate
protocol:Now we just need to implement the methods of the protocol itself. Add these method stubs to the bottom of the class. We will come back to implement
didReceive(event:)
later:The
watch()
functions ofRemoteMongoCollection
return aChangeStreamSession
that we must hold on to as long as we want to keep watching. We can hold onto this session at the instance level ofTodoTableViewController
. Add this to the top of the class near the other member declarations:We’ll now implement
addWatchToCollection()
.Stitch allows you to watch with a filter, so that you are only notified of some events according to that filter. If we wanted to watch with a filter on the items collection, we would use the watch(matchFilter:delegate:) method on the collection. There are two arguments: the first is a BSON document representing the match expression against the incoming
ChangeEvent
; the second is theChangeStreamDelegate
instance that will handle events. Since ourTodoTableViewController
now conforms to theChangeStreamDelegate
protocol, we can pass the current instance represented byself
. The code would look something like this:Note
The following code is an example of how we would use watch with a filter. As you’ll see below, this is not what we want to do in this case.
When you first set up your Stitch backend app, you configured a rule that prevents a user from reading from or writing to another user’s items. Because of this rule, you don’t actually need to use
watch(matchFilter:delegate:)
, since the filter is essentially redundant. In fact, this filter has another issue: only insert and update events will come through – not delete events. The filter we created matches theuserId
against thefullDocument.owner_id
field, but when a document is deleted, the change event does not include thefullDocument
field at all. Therefore, the delete change event would not pass the filter.In this case, we actually just want to use watch(delegate:) to accomplish the goal of watching the collection for changes:
In the
didReceive
method we added to conform to theChangeStreamDelegate
protocol, add your business logic for how you want to handle change notifications. In this tutorial, we’ll update the list of todo items according to whether it’s a new item, updated item, or deleted item, then refresh the list on the UI thread to display the updates.Finally, call the
addWatchToCollection
method from theinit
method after verifying that a user is logged in:
A. Update your client
For the client work in this tutorial, you can start with the app you built in the previous tutorial or start with an app that already has some of the JavaScript structural work done for you.
Get the latest code¶
In the MongoDB Stitch web tutorial repo, run the following command to fetch the tagged version for this tutorial. This version of the code is as it should be immediately prior to starting the tutorial.
Open a Change Stream¶
Our first step in adding collection-level watch to the todo
application is to use watch
to open a change stream.
This will allow you to subsequently monitor and access real-time
changes to your todo items.
Navigate to your stitch/mongodb.js
file and add the following
watchItems
function. This function uses the watch
method to
open a change stream on the items
collection. This is
asynchronous, so it returns a Promise that will eventually resolve to
the stream. We then define two functions: one that lets us access the
change stream after it resolves and another to close the stream when
the user logs out or quits the app.
Note
When you first built the todo backend, you configured a rule that
prevents a user from reading from, or writing to, another user’s
items. Because of this rule, you don’t need to use the
watchWithFilter
function–Stitch will only
send change events for documents where the owner_id
field is
the same as the user_id
field.
Add a Watch Listener¶
At this point, we have a function, watchItems
, that lets us know
when a todo item is updated, added, or deleted. However, we have not
yet taken the steps to reflect any of these changes in our todo app.
In the following steps, we will set up a listener for these change
events and dispatch state updates to reflect these changes in our
application.
First, import the
watchItems
function you just wrote touseTodoItems.js
. We’ll use its resulting change stream in our listener.Now, let’s add the skeleton of our hook,
useWatchItems
, which will implement the listener. Here, we use the stream returned bywatchItems
to identify the type of change event that has occurred. Copy this function intouseTodoItems.js
, right belowloadTodos
.Now, it’s time to write the body of our switch statement in
useWatchItems
. The reducer has cases forinsert
anddelete
, so we’ll start by handling those cases. In the pre-existing code, we already calldispatch
in the same functions as the database calls. To avoid calling an operation twice (which would cause errors), remove the dispatch lines from every function below the listener you just added. Please note that thedispatch
line should remain inloadTodos
.Now, we can safely move the appropriate dispatches to our listener. Add the following lines of code to
useWatchItems
:Finally, call
useWatchItems
at the end of theuseTodoItems
function, right above thereturn
statement. After doing so, the end ofuseTodoItems.js
should look roughly like this:
Add updateTodo
¶
The app should now automatically update any time a todo item is
added even if it’s from another device. To test this, open the app
in another tab/window and add a new todo item. It works! Now try to
we’re only listening for insert and delete change events, and
we only dispatch state events in the listener. Therefore, we need to
add a case for update
to the listener.
Add the following code for
updateTodo
to the reducer inuseTodoItems.js
. This function updates the state of existing todo items.Finally, we’ll add the case handler to the change stream listener to dispatch the event we just added to the reducer. Add the
update
case touseWatchItems
:
B. Build and Test Your App¶
To verify that Watch is working, you need to make changes to the items
collection while your app is running. There are several ways to accomplish
this; while your client app is running in an emulator or on a device,
you can:
- run another instance of the app in a separate emulator or device,
- run the app on a different platform,
- use Compass or the
Atlas UI to directly modify the
items
collection, or - create a Stitch Function in your backend app, running as System, to change documents.
For this tutorial, we’ll add a Function to change the data for us. To do this, open your Stitch app, navigate to the Functions section, and follow these steps:
Click Create New Function
In the Function Settings:
- Name the Function
changeMyRandomDoc
. - Enable Run As System.
- Name the Function
Switch to the Function Editor tab and paste the following code into the editor, replacing all of the existing placeholder code:
Save the changeMyRandomDoc function.
You can now test the functionality by ether calling the function directly from the Function Editor or from another app.
To make it easy, we have hosted a basic web app here that you can use for testing. In the web app, follow these steps:
- Enter your Stitch app id and the id of the user who is logged in to your device app
- Click the Connect to my stitch app button
- Click the Change one of my items button.
The page will show the function’s progress, and you will see the notification in your client app.
Summary¶
Congratulations! Your client app will now be notified every time an item in the collection is updated.