Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(DOCSP-35302): C++: Add new sync control methods #3148

Merged
merged 5 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/cpp/sync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ add_executable(examples-sync
flexible-sync.cpp
quick-start.cpp
sync-errors.cpp
sync-session.cpp
)

target_link_libraries(examples-sync PRIVATE Catch2::Catch2WithMain)
Expand Down
20 changes: 2 additions & 18 deletions examples/cpp/sync/quick-start.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,8 @@ TEST_CASE("sync quick start", "[realm][write][sync][sync-logger]") {
// :snippet-start: open-synced-realm
auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
// :snippet-start: sync-session
auto syncSession = realm.get_sync_session();
// :snippet-end:
// :snippet-start: sync-state
syncSession->state();
// :snippet-end:
// :remove-start:
syncSession->wait_for_download_completion().get();
realm.get_sync_session()->wait_for_download_completion().get();
realm.refresh();
// Remove any existing subscriptions before adding the one for this example
auto clearInitialSubscriptions =
Expand All @@ -150,13 +144,6 @@ TEST_CASE("sync quick start", "[realm][write][sync][sync-logger]") {
.get();
// :snippet-end:
CHECK(updateSubscriptionSuccess == true);
// We don't actually need this here - we use it up above
// in the Bluehawk remove block, but adding it to show
// a relevant Bluehawked example in the docs
// :snippet-start: wait-for-download
syncSession->wait_for_download_completion().get();
realm.refresh();
// :snippet-end:
// :snippet-start: write-to-synced-realm
auto todo = realm::Sync_Todo{.name = "Create a Sync todo item",
.status = "In Progress",
Expand All @@ -169,10 +156,7 @@ TEST_CASE("sync quick start", "[realm][write][sync][sync-logger]") {
CHECK(todos.size() == 1);
auto specificTodo = todos[0];
realm.write([&] { realm.remove(specificTodo); });

// :snippet-start: wait-for-upload
syncSession->wait_for_upload_completion().get();
// :snippet-end:
realm.get_sync_session()->wait_for_upload_completion().get();
}

// :replace-end:
97 changes: 97 additions & 0 deletions examples/cpp/sync/sync-session.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include <catch2/catch_test_macros.hpp>
#include <cpprealm/sdk.hpp>

static const std::string APP_ID = "cpp-tester-uliix";

// :replace-start: {
// "terms": {
// "Local_": "",
// "Sync_": ""
// }
// }

namespace realm {
struct Sync_Todo {
realm::primary_key<realm::object_id> _id{realm::object_id::generate()};
std::string name;
std::string status;
std::string ownerId;
};
REALM_SCHEMA(Sync_Todo, _id, name, status, ownerId);
} // namespace realm

TEST_CASE("sync session", "[realm][write][sync]") {
auto appConfig = realm::App::configuration();
appConfig.app_id = APP_ID;
auto app = realm::App(appConfig);
auto user = app.login(realm::App::credentials::anonymous()).get();
auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
// :snippet-start: sync-session
auto syncSession = realm.get_sync_session();
// :snippet-end:
// :snippet-start: sync-state
syncSession->state();
// :snippet-end:
// :snippet-start: wait-for-download
syncSession->wait_for_download_completion().get();
realm.refresh();
// :snippet-end:
// Remove any existing subscriptions before adding the one for this example
auto clearInitialSubscriptions =
realm.subscriptions().update([](auto& subs) { subs.clear(); }).get();
CHECK(clearInitialSubscriptions == true);
CHECK(realm.subscriptions().size() == 0);
// For this example, get the userId for the Flexible Sync query
auto userId = user.identifier();
auto subscriptions = realm.subscriptions();
auto updateSubscriptionSuccess =
subscriptions
.update([&](realm::mutable_sync_subscription_set& subs) {
subs.add<realm::Sync_Todo>("todos", [&userId](auto& obj) {
// For this example, get only Sync_Todo items where the ownerId
// property value is equal to the userId of the logged-in user.
return obj.ownerId == userId;
});
})
.get();
CHECK(updateSubscriptionSuccess == true);
auto todo = realm::Sync_Todo{.name = "Create a Sync todo item",
.status = "In Progress",
.ownerId = userId};

realm.write([&] { realm.add(std::move(todo)); });

// :snippet-start: pause
syncSession->pause();
// :snippet-end:
// :snippet-start: connection-state
syncSession->connection_state();
// :snippet-end:
CHECK(syncSession->connection_state() ==
realm::internal::bridge::sync_session::connection_state::disconnected);
auto todos = realm.objects<realm::Sync_Todo>();
CHECK(todos.size() == 1);
auto specificTodo = todos[0];
realm.write([&] { realm.remove(specificTodo); });
// :snippet-start: resume
syncSession->resume();
// :snippet-end:
// :snippet-start: observe-connection-change
auto connectionToken = syncSession->observe_connection_change(
[&](enum realm::sync_session::connection_state,
enum realm::sync_session::connection_state new_state) {
// Register a block to execute when connection state changes.
});
// :snippet-end:
// :snippet-start: reconnect
syncSession->reconnect();
// :snippet-end:
// :snippet-start: unregister-observation-token
syncSession->unregister_connection_change_observer(connectionToken);
// :snippet-end:
// :snippet-start: wait-for-upload
syncSession->wait_for_upload_completion().get();
// :snippet-end:
}
// :replace-end:
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
auto syncSession = realm.get_sync_session();
syncSession->state();
// For this example, get the userId for the Flexible Sync query
auto userId = user.identifier();
auto subscriptions = realm.subscriptions();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->connection_state();
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
auto connectionToken = syncSession->observe_connection_change(
[&](enum realm::sync_session::connection_state,
enum realm::sync_session::connection_state new_state) {
// Register a block to execute when connection state changes.
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->pause();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->reconnect();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->resume();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->unregister_connection_change_observer(connectionToken);
8 changes: 8 additions & 0 deletions source/sdk/cpp/quick-start.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
Quick Start - C++ SDK
=====================

.. meta::
:keywords: code example
:description: Learn how to quickly get started with Atlas Device SDK for C++.

.. facet::
:name: genre
:values: tutorial

.. contents:: On this page
:local:
:backlinks: none
Expand Down
131 changes: 126 additions & 5 deletions source/sdk/cpp/sync/manage-sync-session.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
Manage a Sync Session - C++ SDK
===============================

.. meta::
:keywords: code example
:description: Check and manage network and Device Sync connection state through the sync session.

.. facet::
:name: genre
:values: reference

.. contents:: On this page
:local:
:backlinks: none
Expand Down Expand Up @@ -39,26 +47,102 @@ You can use the member function :cpp-sdk:`get_sync_session()
object for any synced realm. The SDK returns this object as an optional.
It is a lightweight handle that you can pass around by value.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.sync-session.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.sync-session.cpp
:language: cpp

.. _cpp-check-network-connection:

Check the Network Connection
----------------------------

.. tip::

The SDK's *offline-first* design means that you generally don't
need to check the current network connection state. That said, the
``connection_state()`` property is available if your app calls for some
indication of connection state.

To check the connection state, you can read the
:cpp-sdk:`sync session instance's <structrealm_1_1internal_1_1bridge_1_1sync__session.html>`
``connection_state()`` property directly.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.connection-state.cpp
:language: cpp

You can also observe the connection state with the
:cpp-sdk:`observe_connection_change() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#a38096e71024b3fd252d3356af5eee113>`
function. This function registers a callback that the SDK invokes when the
underlying sync session changes its connection state.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.observe-connection-change.cpp
:language: cpp

If you register a connection change listener, you can unregister it when
you're done listening for changes. Call the sync session instance's
:cpp-sdk:`unregister_connection_change_observer() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#a86b911bc662f02f607c5e9513d80c0c7>`
method to unregister an observation token.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.unregister-observation-token.cpp
:language: cpp

The network connection state is distinct from the Device Sync connection state
that you can check with the ``state()`` method. For more information about
sync connection state, refer to the Check the Sync State documentation on
this page.

.. _cpp-pause-resume-sync-session:

Pause or Resume a Sync Session
------------------------------

You can pause and resume the sync session on the realm.
Pausing a sync session only suspends that realm's sync session. If you have
more than one open realm, suspend does not affect the sync sessions for
other realms.

To pause a sync session, call the sync session's
:cpp-sdk:`pause() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#ab187b267f0391c8262a0edcff32fcc29>`
method.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.pause.cpp
:language: cpp

To resume a sync session, call the sync session's
:cpp-sdk:`resume() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#a253ef9e08d9f4cf7c42edfd6b6f6df80>`
method.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.resume.cpp
:language: cpp

When to Pause a Sync Session
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. include:: /includes/when-to-pause-sync.rst

.. _cpp-sync-wait-for-changes:

Wait for Changes to Upload and Download
---------------------------------------

You can use the
:cpp-sdk:`sync_session <structrealm_1_1internal_1_1bridge_1_1sync__session.html>`'s
``wait_for_upload_completion()`` and ``wait_for_download_completion()``
methods to wait for changes to upload to or download from Atlas. Both of these
methods can optionally take a callback to execute when upload or download
is complete.

To wait for all changes to upload to Atlas from your synced realm,
use the member function ``.wait_for_upload_completion()``.
use the member function ``wait_for_upload_completion()``.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.wait-for-upload.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.wait-for-upload.cpp
:language: cpp

To wait for all changes from Atlas
to download to your synced realm, use the member function
``wait_for_download_completion()``. Refresh the realm after downloading
dacharyc marked this conversation as resolved.
Show resolved Hide resolved
any changes to be sure it reflects the most recent data.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.wait-for-download.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.wait-for-download.cpp
:language: cpp

.. _cpp-check-sync-state:
Expand All @@ -71,5 +155,42 @@ You can use the :cpp-sdk:`sync_session
member function ``state()`` to check whether the sync session is active.
This returns an enum whose value reflects possible Device Sync states.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.sync-state.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.sync-state.cpp
:language: cpp

The sync connection state is distinct from the network connection state
that you can check with the ``connection_state()`` method. For more information
about network connection state, refer to the Check the Network Connection
documentation on this page.

.. _cpp-reconnect-sync-sessions:

Manually Reconnect All Sync Sessions
------------------------------------

Realm automatically detects when a device regains connectivity after being
offline and attempts to reconnect using an incremental backoff strategy.

You can choose to manually trigger a reconnect attempt with a sync session's
:cpp-sdk:`reconnect() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#af5557f2a2ae8869e6a129afc264e8695>`
method instead of waiting for the duration of the incremental backoff. This is
useful if you have a more accurate understanding of the network conditions
and don't want to rely on Realm's automatic reconnect detection.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.reconnect.cpp
:language: cpp

When you call this method, the SDK forces all sync sessions to *attempt* to
reconnect immediately. This resets any timers used for incremental
backoff.

Calling this method does not guarantee the device can reconnect. If the SDK
gets a fatal error, or if the device is already connected or is trying to
connect, calling this method has no effect.

.. important:: Cannot Reconnect Within Socket Read Timeout Duration

Realm has an internal default socket read timeout of 2 minutes, where
Realm will time out if a read operation does not receive any data
within a 2-minute window. If you call ``reconnect()`` within that window,
the SDK does *not* attempt to reconnect.
Loading