diff --git a/examples/cpp/sync/CMakeLists.txt b/examples/cpp/sync/CMakeLists.txt index 6e06ee19dd..cebcb990b0 100644 --- a/examples/cpp/sync/CMakeLists.txt +++ b/examples/cpp/sync/CMakeLists.txt @@ -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) diff --git a/examples/cpp/sync/quick-start.cpp b/examples/cpp/sync/quick-start.cpp index 50d5ce4788..8434258442 100644 --- a/examples/cpp/sync/quick-start.cpp +++ b/examples/cpp/sync/quick-start.cpp @@ -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 = @@ -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", @@ -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: diff --git a/examples/cpp/sync/sync-session.cpp b/examples/cpp/sync/sync-session.cpp new file mode 100644 index 0000000000..fc8e637ecd --- /dev/null +++ b/examples/cpp/sync/sync-session.cpp @@ -0,0 +1,97 @@ +#include +#include + +static const std::string APP_ID = "cpp-tester-uliix"; + +// :replace-start: { +// "terms": { +// "Local_": "", +// "Sync_": "" +// } +// } + +namespace realm { +struct Sync_Todo { + realm::primary_key _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("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(); + 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: diff --git a/source/examples/generated/cpp/quick-start.snippet.open-synced-realm.cpp b/source/examples/generated/cpp/quick-start.snippet.open-synced-realm.cpp index c2b0146bf8..1f09a12478 100644 --- a/source/examples/generated/cpp/quick-start.snippet.open-synced-realm.cpp +++ b/source/examples/generated/cpp/quick-start.snippet.open-synced-realm.cpp @@ -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(); diff --git a/source/examples/generated/cpp/sync-session.snippet.connection-state.cpp b/source/examples/generated/cpp/sync-session.snippet.connection-state.cpp new file mode 100644 index 0000000000..3a189b267c --- /dev/null +++ b/source/examples/generated/cpp/sync-session.snippet.connection-state.cpp @@ -0,0 +1 @@ +syncSession->connection_state(); diff --git a/source/examples/generated/cpp/sync-session.snippet.observe-connection-change.cpp b/source/examples/generated/cpp/sync-session.snippet.observe-connection-change.cpp new file mode 100644 index 0000000000..79579b9a0b --- /dev/null +++ b/source/examples/generated/cpp/sync-session.snippet.observe-connection-change.cpp @@ -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. + }); diff --git a/source/examples/generated/cpp/sync-session.snippet.pause.cpp b/source/examples/generated/cpp/sync-session.snippet.pause.cpp new file mode 100644 index 0000000000..5da4adf8b8 --- /dev/null +++ b/source/examples/generated/cpp/sync-session.snippet.pause.cpp @@ -0,0 +1 @@ +syncSession->pause(); diff --git a/source/examples/generated/cpp/sync-session.snippet.reconnect.cpp b/source/examples/generated/cpp/sync-session.snippet.reconnect.cpp new file mode 100644 index 0000000000..1a156cd4ba --- /dev/null +++ b/source/examples/generated/cpp/sync-session.snippet.reconnect.cpp @@ -0,0 +1 @@ +syncSession->reconnect(); diff --git a/source/examples/generated/cpp/sync-session.snippet.resume.cpp b/source/examples/generated/cpp/sync-session.snippet.resume.cpp new file mode 100644 index 0000000000..3b48b7134b --- /dev/null +++ b/source/examples/generated/cpp/sync-session.snippet.resume.cpp @@ -0,0 +1 @@ +syncSession->resume(); diff --git a/source/examples/generated/cpp/quick-start.snippet.sync-session.cpp b/source/examples/generated/cpp/sync-session.snippet.sync-session.cpp similarity index 100% rename from source/examples/generated/cpp/quick-start.snippet.sync-session.cpp rename to source/examples/generated/cpp/sync-session.snippet.sync-session.cpp diff --git a/source/examples/generated/cpp/quick-start.snippet.sync-state.cpp b/source/examples/generated/cpp/sync-session.snippet.sync-state.cpp similarity index 100% rename from source/examples/generated/cpp/quick-start.snippet.sync-state.cpp rename to source/examples/generated/cpp/sync-session.snippet.sync-state.cpp diff --git a/source/examples/generated/cpp/sync-session.snippet.unregister-observation-token.cpp b/source/examples/generated/cpp/sync-session.snippet.unregister-observation-token.cpp new file mode 100644 index 0000000000..09bba2e0e9 --- /dev/null +++ b/source/examples/generated/cpp/sync-session.snippet.unregister-observation-token.cpp @@ -0,0 +1 @@ +syncSession->unregister_connection_change_observer(connectionToken); diff --git a/source/examples/generated/cpp/quick-start.snippet.wait-for-download.cpp b/source/examples/generated/cpp/sync-session.snippet.wait-for-download.cpp similarity index 100% rename from source/examples/generated/cpp/quick-start.snippet.wait-for-download.cpp rename to source/examples/generated/cpp/sync-session.snippet.wait-for-download.cpp diff --git a/source/examples/generated/cpp/quick-start.snippet.wait-for-upload.cpp b/source/examples/generated/cpp/sync-session.snippet.wait-for-upload.cpp similarity index 100% rename from source/examples/generated/cpp/quick-start.snippet.wait-for-upload.cpp rename to source/examples/generated/cpp/sync-session.snippet.wait-for-upload.cpp diff --git a/source/sdk/cpp/quick-start.txt b/source/sdk/cpp/quick-start.txt index 6fd6ff1099..89e283aea3 100644 --- a/source/sdk/cpp/quick-start.txt +++ b/source/sdk/cpp/quick-start.txt @@ -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 diff --git a/source/sdk/cpp/sync/manage-sync-session.txt b/source/sdk/cpp/sync/manage-sync-session.txt index 7d7b160426..a4dd5444f6 100644 --- a/source/sdk/cpp/sync/manage-sync-session.txt +++ b/source/sdk/cpp/sync/manage-sync-session.txt @@ -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 @@ -39,18 +47,94 @@ 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 ` +``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() ` +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() ` +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() ` +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() ` +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 `'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 @@ -58,7 +142,7 @@ to download to your synced realm, use the member function ``wait_for_download_completion()``. Refresh the realm after downloading 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: @@ -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() ` +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.