Docs Menu

Sync Changes Between Devices - iOS SDK

On this page

The typical flow for opening a synced realm involves:

  1. Authenticating the user
  2. Creating a sync configuration
  3. Opening the user's synced realm with the configuration.

At authentication, we cache user credentials in a sync_metadata.realm file on device.

When you open a synced realm after authenticating, you can bypass the login flow and go directly to opening the synced realm, using the same sync configuration you already created.

With cached credentials, you can:

  • Open a synced realm immediately with the data that is on the device. You can use this method offline or online.
  • Open a synced realm after downloading changes from your Realm app. This requires the user to have an active internet connection.

Synced realms differ from local Realm Database in a few ways:

  • Synced realms attempt to sync changes with your backend Realm app, whereas local realms do not.
  • Synced realms can be accessed by authenticated users, while local realms do not require any concept of users or authentication.
  • With synced realms, you can specify the download behavior to download updates before opening a realm. However, this requires users to be online. Local realms - with no sync capability - can always be used offline.

You can copy data from a local Realm Database to a synced realm, but you cannot sync a local Realm Database. You must initialize a synced realm with a sync configuration.

New in version 10.15.0.

When you open a synced realm with the Swift SDK, you can pass the downloadBeforeOpen parameter to specify whether to download the changeset from your Realm app before opening the realm. This parameter accepts a case from the OpenBehavior enum:

  • never: Immediately open the realm on the device. Download changes in the background when the user has internet, but don't block opening the realm.
  • always: Check for changes every time you open the realm. Requires the user to have an active internet connection.
  • once: Download data before opening a realm for the first time, but open it without downloading changes on subsequent opens. This lets you populate a realm with initial data, but enables offline-first functionality on subsequent opens.
func testSpecifyDownloadBehavior() async throws {
let app = App(id: YOUR_REALM_APP_ID)
let user = try await app.login(credentials: Credentials.anonymous)
let partitionValue = "some partition value"
var configuration = user.configuration(partitionValue: partitionValue)
let realm = try await Realm(configuration: configuration, downloadBeforeOpen: .always)
print("Successfully opened realm after downloading: \(realm)")
}

New in version 10.12.0.

SwiftUI apps can use RealmSwift property wrappers to open realms and populate views. These property wrappers provide a syntax that integrates with SwiftUI to let developers declare whether their clients should download changesets before opening realms or open realms with data on the devices.

Apps that require up-to-date information from the server, such as game apps with live leaderboards that the user can play on multiple devices, should use @AsyncOpen to open a realm. This only works if the user has a network connection when opening the realm, but ensures the user is never using the app with stale data.

Apps where it's not a problem for the user to work with potentially stale data, such as note-taking apps where users should be able to work with data on the device, should use @AutoOpen to open the realm. Like @AsyncOpen, this attempts to download updates before opening the realm. However, if a network connection is not available, this method instead opens a realm with data on the device. This property wrapper enables developers to design offline-first capabilities into their apps.

To download updates from your Realm app before opening a realm, use the @AsyncOpen property wrapper. This requires the user to have a network connection.

@AsyncOpen(appId: "app_id", partitionValue: <partition_value>) var asyncOpen

This SwiftUI property wrapper initiates Realm.asyncOpen() for the current user. The property wrapper publishes states, represented by the AsyncOpenState enum, which you can use to update the view.

Example

This example illustrates one way you might use @AsyncOpen to open a realm in a view. First, check for a user, or log them in. Then, attempt to open the realm, switching on the AsyncOpenState to display an appropriate view. When the realm opens successfully, inject it as an environment value to populate the view.

struct AsyncOpenView: View {
// @AutoOpen attempts to connect to the server and download remote changes
// before the realm opens, which might take a moment.
// We can use an empty string as the partitionValue here because we're
// injecting the user.id as an environment value from the LoginView.
@AsyncOpen(appId: YOUR_REALM_APP_ID_HERE, partitionValue: "", timeout: 4000) var asyncOpen
var body: some View {
// Switch on the AsyncOpenState enum to update the view
// based on AsyncOpen progress.
switch asyncOpen {
// Starting the Realm.asyncOpen process.
// Show a progress view.
case .connecting:
ProgressView()
// Waiting for a user to be logged in before executing Realm.asyncOpen
case .waitingForUser:
ProgressView("Waiting for user to log in...")
// The realm has been opened and is ready for use.
// Show the content view.
case .open(let realm):
ListView()
.environment(\.realm, realm)
// The realm is currently being downloaded from the server.
// Show a progress view.
case .progress(let progress):
ProgressView(progress)
// Opening the Realm failed.
// Show an error view.
case .error(_):
ErrorView()
}
}
}

To open a synced realm regardless of whether the user has a network connection, use the @AutoOpen property wrapper.

@AutoOpen(appId: "app_id", partitionValue: <partition_value>) var autoOpen

This SwiftUI property wrapper attempts to initiate a Realm.asyncOpen() for the current user. If there is no internet connection, this property wrapper instead returns an opened realm for the given appId and partitionValue.

The property wrapper publishes states, represented by the AsyncOpenState enum, which you can use to update the view.

Example

This example illustrates one way you might use @AutoOpen to open a realm in a view. First, check for a user, or log them in. Then, attempt to open the realm, switching on the AsyncOpenState to display an appropriate view. When the realm opens successfully, inject it as an environment value to populate the view.

struct AutoOpenView: View {
// @AutoOpen attempts to connect to the server and download remote changes
// before the realm opens, which might take a moment. However, if there is
// no network connection, AutoOpen will open a realm on the device.
// We can use an empty string as the partitionValue here because we're
// injecting the user.id as an environment value from the LoginView.
@AutoOpen(appId: YOUR_REALM_APP_ID_HERE, partitionValue: "", timeout: 4000) var autoOpen
var body: some View {
// Switch on the AsyncOpenState enum to update the view
// based on AutoOpen progress.
switch autoOpen {
// Starting the Realm.asyncOpen process.
// Show a progress view.
case .connecting:
ProgressView()
// Waiting for a user to be logged in before executing Realm.asyncOpen
case .waitingForUser:
ProgressView("Waiting for user to log in...")
// The realm has been opened and is ready for use.
// Show the content view.
case .open(let realm):
ListView()
.environment(\.realm, realm)
// The realm is currently being downloaded from the server.
// Show a progress view.
case .progress(let progress):
ProgressView(progress)
// Opening the Realm failed.
// Show an error view.
case .error(_):
ErrorView()
}
}
}

You can inject the .partitionValue environment value into the view where you are performing the @AsyncOpen or @AutoOpen to populate the partitionValue:

AsyncOpenView()
.environment(\.partitionValue, user.id)

You can inject the opened realm into your content view as an environment value. The property wrapper uses this realm to populate the view:

ListView()
.environment(\.realm, realm)

If you want your app to update data in the background (while the app is minimized), iOS requires you to implement Background App Refresh. Enabling Background App Refresh minimizes the time it takes for the user to see the most recent data; without Background App Refresh, MongoDB Realm updates the data when the user launches the app, potentially resulting in noticeable lag.

To use the realm while the device is locked, you must adjust the file protection settings. See Use Realm When the Device Is Locked.

Opening a synced realm starts a SyncSession for that realm. You can suspend 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.

Note

Use the .suspend() method to control when a device syncs. You should only use it for temporary and short-term pauses of syncing.

Examples of when to use .suspend() include:

  • Syncing data only at specified time of day
  • Conserving device battery use

Don't use the .suspend() method to stop syncing for indefinite time periods or time ranges in months and years. The functionality is not designed or tested for these use cases, and you could encounter a range of issues when using it this way.

Tip

MongoDB Realm's offline-first design means that you generally don't need to check the current network connection state. That said, the connectionState property is available if your app calls for some indication of connection state.

While developing an application that uses Realm Sync, you should set an error handler. This error handler will detect and respond to any failed sync-related API calls.

In some cases, you may want to completely delete a realm file from disk.

Realm avoids copying data into memory except when absolutely required. As a result, all objects managed by a realm have references to the file on disk. Before you can safely delete the file, you must ensure the deallocation of these objects:

  • All objects read from or added to the realm
  • All List and Results objects
  • All ThreadSafeReference objects
  • The realm itself

In practice, there are two safe times to delete the realm file:

  1. On application startup before ever opening the realm.
  2. After only having opened the realm within an explicit autorelease pool, which ensures deallocation of all of objects within it.
Tip

To perform a client reset, see Client Resets - iOS SDK.

Tip

See RLMSyncLogLevel for a description of each available log level. Note that more logging can negatively affect performance.

Tip

To diagnose and troubleshoot errors while developing your application, set the log level to debug or trace. For production deployments, decrease the log level for improved performance.

Give Feedback
© 2021 MongoDB, Inc.

About

  • Careers
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2021 MongoDB, Inc.