Docs Menu
Docs Home
/ /
Atlas Device SDKs
/ /

Configure & Open a Synced Realm - Swift SDK

On this page

  • Synced Realms
  • Synced Realms vs. Non-Synced Realms
  • Open a Synced Realm
  • Open a Synced Realm In Memory
  • Open a Synced Realm as a Different Sync User
  • Open a Synced Realm with Swift Concurrency Features
  • Download Changes Before Open
  • Open a Synced Realm Offline

You can configure a realm to automatically synchronize data between many devices that each have their own local copy of the data. Synced realms use a different configuration than local-only realms and require an Atlas App Services backend to handle the synchronization process.

Applications can always create, modify, and delete synced realm objects locally, even when offline. Whenever a network connection is available, the Realm SDK opens a connection to an application server and syncs changes to and from other clients. The Atlas Device Sync protocol and server-side operational transforms guarantee that all fully synced instances of a realm see exactly the same data, even if some changes occurred offline and/or were received out of order.

Tip

Learn How to Configure and Use Sync

For more information on Device Sync, including directions on how to set up sync in an App Services app, see Sync Data.

Synced realms differ from non-synced local realm in a few ways:

  • Synced realms attempt to sync changes with your backend App Services App, whereas non-synced realms do not.

  • Synced realms can be accessed by authenticated users, while non-synced realms have no concept of users or authentication.

  • With synced realms, you can specify the download behavior to download updates before opening a realm. However, requiring changes to download before opening the realm requires the user to be online. Non-synced realms can always be used offline.

You can copy data from a non-synced realm to a synced realm, and vice versa, but you cannot sync a non-synced realm. To convert a non-synced realm to a synced realm, you can follow the process described in Migrate a Local-Only App to a Sync-Enabled App.

For more information about how to configure and open a non-synced realm, refer to: Configure & Open a Realm - Swift SDK.

Tip

See also:

For information about opening a Partition-Based Sync realm, refer to Open a Partition-Based Sync Realm.

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 App. This requires the user to have an active internet connection.

When you use Flexible Sync, use the flexibleSyncConfiguration() to open a synced realm.

Tip

If your app accesses Realm in an async/await context, mark the code with @MainActor to avoid threading-related crashes.

let realm = try await openFlexibleSyncRealm()
// Opening a realm and accessing it must be done from the same thread.
// Marking this function as `@MainActor` avoids threading-related issues.
@MainActor
func openFlexibleSyncRealm() async throws -> Realm {
let app = App(id: APPID)
let credentials = emailPasswordCredentials(app: app)
let user = try await app.login(credentials: credentials)
var config = user.flexibleSyncConfiguration()
// Pass object types to the Flexible Sync configuration
// as a temporary workaround for not being able to add complete schema
// for a Flexible Sync app
config.objectTypes = [Task.self, Team.self]
let realm = try await Realm(configuration: config, downloadBeforeOpen: .always)
print("Successfully opened realm: \(realm)")
return realm
}

Important

Flexible Sync Requires a Subscription

If your app uses bidirectional, standard Device Sync, you can't use a Flexible Sync realm until you add at least one subscription. To learn how to add subscriptions, see: Add a Subscription.

This does not apply to Data Ingest. You cannot create a subscription for an AsymmetricObject.

New in version 10.46.0.

You can open a synced database entirely in memory, which does not create a .realm file or its associated auxiliary files. Instead the SDK stores objects in memory while the realm is open and discards them immediately when all instances are closed.

To open a synced realm in memory, set the inMemoryIdentifier property of the realm configuration to a string identifier.

// Instantiate the app and get a user.
let app = App(id: APPID)
let user = try await app.login(credentials: Credentials.anonymous)
// Create a configuration.
var configuration = user.flexibleSyncConfiguration()
configuration.objectTypes = [Task.self, Team.self]
// Specify an in-memory identifier for the configuration.
configuration.inMemoryIdentifier = "YOUR-IDENTIFIER-STRING"
// Open a Realm with this configuration.
let realm = try await Realm(configuration: configuration)
print("Successfully opened realm: \(realm)")
// Add subscriptions and work with the realm

New in version 10.23.0.

If you want to open a synced realm as a different Sync user, you can use the writeCopy(configuration: ) method to make a copy of the synced realm for use with the new user's sync configuration. The example below creates a copy of the synced realm, with all of its existing data, that you can use with a different sync configuration.

After you copy the realm for the new Sync user's configuration, you can open the copy as a synced realm for that user.

Note

Same-Type Sync Only

This method only supports copying a Partition-Based Sync configuration for another Partition-Based Sync user, or a Flexible Sync configuration for another Flexible Sync user. You cannot use this method to convert between a Partition-Based Sync realm and a Flexible Sync realm or vice-versa.

Tip

If your app accesses Realm in an async/await context, mark the code with @MainActor to avoid threading-related crashes.

try await convertSyncedRealmForAnotherUser()
// Opening a realm and accessing it must be done from the same thread.
// Marking this function as `@MainActor` avoids threading-related issues.
@MainActor
func convertSyncedRealmForAnotherUser() async throws {
let app = App(id: YOUR_APP_SERVICES_APP_ID)
// Log in the user whose realm you want to use with another sync user
let frodoBaggins = try await app.login(credentials: Credentials.anonymous)
var frodoConfig = frodoBaggins.configuration(partitionValue: "Some Partition Value")
frodoConfig.objectTypes = [QsTask.self]
// Open the synced realm, and confirm it contains the data we want
// the other user to be able to access.
let frodoRealm = try await Realm(configuration: frodoConfig, downloadBeforeOpen: .always)
let frodoRealmTasks = frodoRealm.objects(QsTask.self)
let frodoSyncedTasks = frodoRealmTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoSyncedTasks.count, 3)
print("Successfully opened frodo's realm and it contains this many tasks: \(frodoSyncedTasks.count)")
// Log in as the user who will work with frodo's synced realm
let samwiseGamgee = try await app.login(credentials: Credentials.anonymous)
var samConfig = samwiseGamgee.configuration(partitionValue: "Some Partition Value")
samConfig.objectTypes = [QsTask.self]
// Specify an output directory for the copied realm
// We're using FileManager here for tested code examples.
guard let outputDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
// Append a file name to complete the path
let copiedRealmFilePath = outputDir.appendingPathComponent("copied.realm")
// Update the config file path to the path where you want to save the copied realm
samConfig.fileURL = copiedRealmFilePath
// Make a copy of frodo's realm that uses sam's config
try frodoRealm.writeCopy(configuration: samConfig)
// Open sam's realm, and see that it contains the same data as frodo's realm
let samRealm = try await Realm(configuration: samConfig)
let samRealmTasks = samRealm.objects(QsTask.self)
var samSyncedTasks = samRealmTasks.where { $0.owner == "Frodo" }
print("Successfully opened sam's realm and it contains this many tasks: \(samSyncedTasks.count)")
XCTAssertEqual(frodoSyncedTasks.count, samSyncedTasks.count)
// Add a task to sam's realm
let task = QsTask(value: ["name": "Keep an eye on that Gollum", "owner": "Sam"])
try! samRealm.write {
samRealm.add(task)
}
// See that the new task reflects in sam's realm, but not frodo's
samSyncedTasks = samRealmTasks.where { $0.owner == "Sam" }
XCTAssertEqual(samSyncedTasks.count, 1)
let samTasksInFrodoRealm = frodoRealmTasks.where { $0.owner == "Sam" }
XCTAssertEqual(samTasksInFrodoRealm.count, 0)
}

You can open a synced realm as an actor-isolated realm using Swift concurrency features:

@MainActor
func mainThreadFunction() async throws {
// Initialize the app client and authenticate a user
let app = App(id: APPID)
let user = try await app.login(credentials: Credentials.anonymous)
// Configure the synced realm
var flexSyncConfig = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
subs.append(QuerySubscription<Todo>(name: "all_todos"))})
flexSyncConfig.objectTypes = [Todo.self]
// Open and use the synced realm
let realm = try await Realm(configuration: flexSyncConfig, actor: MainActor.shared, downloadBeforeOpen: .always)
try await useTheSyncedRealm(realm: realm)
}

For more information about working with actor-isolated realms, refer to Use Realm with Actors - Swift SDK.

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 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.

Tip

If your app accesses Realm in an async/await context, mark the code with @MainActor to avoid threading-related crashes.

func getRealmAfterDownloadingUpdates() async throws -> Realm {
let app = App(id: APPID)
let user = try await app.login(credentials: Credentials.anonymous)
var configuration = user.flexibleSyncConfiguration()
configuration.objectTypes = [FlexibleSync_Task.self, FlexibleSync_Team.self]
let realm = try await Realm(configuration: configuration, downloadBeforeOpen: .always)
print("Successfully opened realm after downloading: \(realm)")
return realm
}
let realm = try await getRealmAfterDownloadingUpdates()
print("The open realm is: \(realm)")
// Add subscription and work with the realm

When your Realm application authenticates a user, it caches the user's credentials. You can check for existing user credentials to bypass the login flow and access the cached user. Use this to open a realm offline.

Note

Initial login requires a network connection

When a user signs up for your app, or logs in for the first time with an existing account on a client, the client must have a network connection. Checking for cached user credentials lets you open a realm offline, but only if the user has previously logged in while online.

You can only open a synced realm offline if you do not require your client app to always download changes before opening the realm.

Tip

If your app accesses Realm in an async/await context, mark the code with @MainActor to avoid threading-related crashes.

// Log the user into the backend app.
// The first time you login, the user must have a network connection.
func getUser() async throws -> User {
// Check for an existing user.
// If the user is offline but credentials are
// cached, this returns the existing user.
if let user = app.currentUser {
return user
} else {
// If the device has no cached user
// credentials, log them in.
let app = App(id: YOUR_APP_SERVICES_APP_ID)
let loggedInUser = try await app.login(credentials: Credentials.anonymous)
return loggedInUser
}
}
let user = try await getUser()
var configuration = user.configuration(partitionValue: "Some Partition Value")
// Open a Realm with this configuration.
// If you do not require the app to download updates
// before opening the realm, the realm just opens, even if
// offline.
let realm = try await Realm(configuration: configuration)
print("Successfully opened realm: \(realm)")

Back

Add Sync to an App

Next

Manage Flexible Sync Subscriptions