Navigation

Build a Mobile App with Sync

Deprecation

MongoDB has deprecated MongoDB Mobile. We are focusing our efforts on Realm, the most popular database for mobile devices.

Stay up to date and see what we have planned.

Overview

After setting up your Android or iOS project to use the MongoDB Stitch SDKs, you are ready to initialize MongoDB Stitch and connect to the database. The following procedure shows you how to build an app that uses MongoDB Mobile and Atlas to store data.

For an example that uses only MongoDB Mobile to store data in an app, see Build a Local-Only Mobile App.

For a complete example app that uses Sync, see the To-Do tutorial.

Prerequisites

If you have not done so yet, refer to the steps in Set up a MongoDB Mobile Project to set up your Android or iOS project to use the MongoDB Stitch SDKs.

Note

The Stitch SDKs require your Atlas cluster to be version 3.4 or higher. To use Sync, your cluster must be version 3.6 or higher.

Connecting Your App to Stitch and Atlas

To use the Android SDKs to connect to MongoDB Stitch and sync data between an Atlas cluster to your MongoDB Mobile instance, follow these steps:

  1. First, you establish a remote connection to Atlas and create a RemoteMongoCollection, as follows:

    RemoteMongoCollection _remoteCollection;
    
    ...
    
    // Create the StitchAppClient
    final StitchAppClient client = Stitch.initializeAppClient("<APP_ID>");
    
    // Log-in using an Anonymous authentication provider from Stitch
    client.getAuth().loginWithCredential(new AnonymousCredential())
       .addOnCompleteListener(new OnCompleteListener<StitchUser>() {
          @Override
          public void onComplete(@NonNull Task<StitchUser> task) {
                // Get a remote client
                final RemoteMongoClient remoteMongoClient =
                client.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
    
                // Set up the atlas collection
                _remoteCollection = remoteMongoClient
                .getDatabase("my_db").getCollection("my_collection");
          }
       });
    
  2. Next, configure Sync by calling the config() method on the RemoteMongoCollection.sync() interface:

    // Configure automatic data sync between Atlas and local
    // In this example, conflicts are resolved by giving preference
    // to remote changes.
    _remoteCollection.sync().configure(
    DefaultSyncConflictResolvers.remoteWins(),
    new MyUpdateListener(),
    new MyErrorListener());
    
    ...
    
    private class MyErrorListener implements ErrorListener {
    @Override
    public void onError(BsonValue documentId, Exception error) {
          Log.e("Stitch", error.getLocalizedMessage());
          Set<BsonValue> docsThatNeedToBeFixed = _remoteCollection.sync().getPausedDocumentIds();
          for (BsonValue doc_id : docsThatNeedToBeFixed) {
                // Add your logic to inform the user.
                // When errors have been resolved, call
                _remoteCollection.sync().resumeSyncForDocument(doc_id);
          }
          // refresh the app view, etc.
       }
    }
    
    private class MyUpdateListener implements ChangeEventListener<Document> {
    @Override
    public void onEvent(final BsonValue documentId, final ChangeEvent<Document> event) {
          if (!event.hasUncommittedWrites()) {
             // Custom actions can go here
          }
          // refresh the app view, etc.
       }
    }
    

    The Config method takes the following properties:

    • A ConflictHandler, which receives both the remote and local versions of a document when there is a conflict. The Stitch SDKs provides a built-in DefaultSyncConflictResolvers class with two methods, one that uses the local version to resolve the conflict (localWins), and the other that favors the remote version (remoteWins).
    • A ChangeEventListener , which is called whenever a change happens locally or remotely. There is no need to handle the change events yourself, but this provides a hook for you to update your app or perform other change-related logic.
    • An ErrorListener , which receives errors that occur when syncing local changes (for example, if the operation is not permitted by Stitch rules). Your code can check the error, take the appropriate action, and then call resumeSyncForDocument.

    Important

    At this point, for all operations on the synced data, be sure to include sync() in your calls, like this: [remoteCollection].sync().[operation].

  3. You can now use the RemoteMongoCollection.sync() interface to perform CRUD operations. For example, you can insert a document into the local database (which will automatically sync with the remote database), and perform custom logic when the insert is complete:

    final Task<RemoteInsertOneResult> res = _remoteCollection.sync().insertOne(doc);
    res.addOnCompleteListener(new OnCompleteListener<RemoteInsertOneResult>() {
       @Override
       public void onComplete(@NonNull final Task<RemoteInsertOneResult> task) {
             if (task.isSuccessful()) {
                // do something
             } else {
                Log.e(TAG, "Error adding item", task.getException());
             }
       }
    });
    

To use the Swift SDKs to connect to MongoDB Stitch and sync data between an Atlas cluster to your MongoDB Mobile instance, you can use the following code:

  1. First, you establish a remote connection to Atlas and create a RemoteMongoCollection, as follows:

    var itemsCollection: RemoteMongoCollection<TodoItem>!
    let stitch = try! Stitch.initializeAppClient(withClientAppID: "<APP-ID>")
    if !stitch.auth.isLoggedIn {
       doLogin()
    }
    
    ...
    
    private func doLogin() {
       stitch.auth.login(withCredential: AnonymousCredential() {
          switch $0 {
             case .success(let user):
                print("logged in")
                self.loggedIn()
    
                let mongoClient = try! stitch.serviceClient(fromFactory:
                   remoteMongoClientFactory, withName: "mongodb-atlas")
    
                itemsCollection = mongoClient.db("todo").collection(
                   "items", withCollectionType: TodoItem.self)
    
             case .failure(let e):
                print("error logging in \(e)")
          }
       }
    }
    
  2. Next, configure Sync by calling the config method on the RemoteMongoCollection.sync interface.

    The Configure method takes the following properties:

    • A ConflictHandler, which receives both the remote and local versions of a document when there is a conflict. The Stitch SDKs provides a built-in DefaultConflictHandler class with two methods, one that uses the local version to resolve the conflict (localWins), and the other that favors the remote version (remoteWins).
    • A ChangeEventDelegate , which is called whenever a change happens locally or remotely. There is no need to handle the change events yourself, but this provides a hook for you to update your app or perform other change-related logic.
    • An ErrorListener , which receives errors that occur when syncing local changes (for example, if the operation is not permitted by Stitch rules). Your code can check the error, take the appropriate action, and then call resumeSync.

    For example, your code to configure Sync might look like this:

    todoCollection.sync.configure(
      conflictHandler: DefaultConflictHandlers.remoteWins.resolveConflict,
      changeEventDelegate: { documentId, event in
        if !event.hasUncommittedWrites {
          // you can add code here to update your app's UI or
          // perform other operations based on a document change.
        }
      }, errorListener: self.on)
    

    Important

    At this point, for all operations on the synced data, be sure to include sync in your calls, like this: [remoteCollection].sync.[operation].

  3. You can now use the RemoteMongoCollection’s sync interface to perform CRUD operations. For example, you can insert a document into the local database (which will automatically sync with the remote database), and perform custom logic when the insert is complete:

    itemsCollection.sync.insertOne(document: todoItem) { result in
       switch result {
          case .success(_):
             // Any custom actions you want to happen on success
          case .failure(let e):
             fatalError(e.localizedDescription)
       }
    }