Docs Menu

Configure & Open a Realm - Swift SDK

On this page

A realm is the core data structure used to organize data in Realm Database. For more information, see Realms - Swift SDK.

When you open a realm, you can pass a Realm.Configuration that specifies additional details about how to configure the realm file. This includes things like:

  • Whether the file should open at a specific fileURL or in-memory
  • Providing a logged-in user to use Sync with the realm
  • Specifying the realm use only a subset of your app's classes

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

  • Synced realms attempt to sync changes with your backend Atlas 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 Database to a synced realm, but you cannot sync a non-synced Realm Database.

Realm does not have a direct mechanism to add sync to a non-synced realm, or to permanently stop Sync for a synced realm. However, the Swift SDK does provide methods that enable you to copy a realm file for use with a different configuration. With these methods, you can easily duplicate a realm's data, which you can then open with a sync or non-sync configuration. This lets you indirectly add Sync to a non-synced realm, or permanently stop a realm from syncing. See:

You can open a non-synced local realm with several different configuration options:

  • No configuration - i.e. default configuration
  • Specify a file URL for the realm
  • Open the realm only in memory, without saving a file to the file system
  • Copy a synced realm to use without Sync

You can open a realm entirely in memory, which will 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.

Important

When all in-memory realm instances with a particular identifier go out of scope, Realm Database deletes all data in that realm. To avoid this, hold onto a strong reference to any in-memory realms during your app's lifetime.

New in version 10.23.0.

Tip

You may temporarily pause a Sync session if you do not want to permanently change a synced realm to a non-synced realm. See: Suspend or Resume a Sync Session.

If you want to permanently stop a realm from syncing to your Atlas App Services backend, you can use the writeCopy(configuration: ) method to make a copy of a synced realm for use with a non-sync configuration. The example below creates a copy of the realm file, with all of its existing data, at a file URL you specify.

This process removes the realm_id in the local realm. You must increment the schema version as if you had deleted a property.

After you copy the realm for use without Sync, you can open the copy as a non-synced realm. Any changes you make to the non-synced realm reflect only in the local realm file. No changes propogate to other devices or the Atlas App Services backend.

func testConvertSyncToLocal() async throws {
let app = App(id: YOUR_APP_SERVICES_APP_ID)
// Log in the user whose realm you want to open as a local realm
let syncUser = try await app.login(credentials: Credentials.anonymous)
// Create a configuration to open the seed user's realm
var syncConfig = syncUser.configuration(partitionValue: "Some Partition Value")
syncConfig.objectTypes = [QsTask.self]
// Open the realm with the Sync user's config, downloading
// any remote changes before opening.
let syncedRealm = try await Realm(configuration: syncConfig, downloadBeforeOpen: .always)
print("Successfully opened realm: \(syncedRealm)")
// Verify the data we expect in the realm
// The synced realm we are copying contains 3 tasks whose owner is "Frodo"
let syncedTasks = syncedRealm.objects(QsTask.self)
var frodoSyncedTasks = syncedTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoSyncedTasks.count, 3)
print("Synced realm opens and contains this many tasks: \(frodoSyncedTasks.count)")
// Construct an output file path for the local Realm
guard let outputDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
// Append a file name to complete the path
let localRealmFilePath = outputDir.appendingPathComponent("local.realm")
// Construct a local realm configuration
var localConfig = Realm.Configuration()
localConfig.objectTypes = [QsTask.self]
localConfig.fileURL = localRealmFilePath
// `realm_id` will be removed in the local realm, so we need to bump
// the schema version.
localConfig.schemaVersion = 1
// Check to see if there is already a realm at the local realm file path. If there
// is already a realm there, delete it.
if Realm.fileExists(for: localConfig) {
try Realm.deleteFiles(for: localConfig)
print("Successfully deleted existing realm at path: \(localRealmFilePath)")
} else {
print("No file currently exists at path")
}
// Make a copy of the synced realm that uses a local configuration
try syncedRealm.writeCopy(configuration: localConfig)
// Try opening the realm as a local realm
let localRealm = try await Realm(configuration: localConfig)
// Verify that the copied realm contains the data we expect
let localTasks = localRealm.objects(QsTask.self)
var frodoLocalTasks = localTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoLocalTasks.count, 3)
print("Local realm opens and contains this many tasks: \(frodoLocalTasks.count)")
let task = QsTask(value: ["name": "Send gift basket to Tom Bombadil", "owner": "Frodo"])
try! localRealm.write {
localRealm.add(task)
}
frodoLocalTasks = localTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoLocalTasks.count, 4)
print("After adding a task, the local realm contains this many tasks: \(frodoLocalTasks.count)")
frodoSyncedTasks = syncedTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoSyncedTasks.count, 3)
print("After writing to local realm, synced realm contains this many tasks: \(frodoSyncedTasks.count)")
XCTAssertNotEqual(frodoLocalTasks.count, frodoSyncedTasks.count)
}

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

Initialize a synced realm with a sync configuration. This enables you to specify a partition value whose data should sync to the realm.

New in version 10.22.0.

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

let app = App(id: APPID)
let user = try await app.login(credentials: Credentials.anonymous)
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)
Important
Flexible Sync Requires a Subscription

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.

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.

func testConvertSyncToSync() 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)
}

New in version 10.23.0.

If you want a non-synced realm to start syncing with other devices and your Atlas App Services backend, you can use the writeCopy(configuration: ) method to make a copy of the non-synced realm for use with a sync configuration. The example below creates a copy of a non-synced realm file, with all of its existing data, that you can use with a sync configuration.

After you copy the realm for use with Sync, you can open the copy as a synced realm. Any changes you make to the synced realm will reflect in the synced realm file, and they will also propogate to other devices and the Atlas App Services backend.

func testConvertLocalToSync() async throws {
let app = App(id: YOUR_APP_SERVICES_APP_ID)
// Log in the user whose realm you want to open as a synced realm
let syncUser = try await app.login(credentials: Credentials.anonymous)
// Create a configuration to open the sync user's realm
var syncConfig = syncUser.configuration(partitionValue: "Your Partition Value")
syncConfig.objectTypes = [QsTask.self]
// Prepare the configuration for the user whose local realm you
// want to convert to a synced realm
var localConfig = Realm.Configuration()
localConfig.objectTypes = [QsTask.self]
// For this example, add some data to the local realm
// before copying it. No need to do this if you're
// copying a realm that already contains data.
let localRealm = addExampleData(config: localConfig)
// Create a copy of the local realm that uses the
// sync configuration. All the data that is in the
// local realm is available in the synced realm.
try! localRealm.writeCopy(configuration: syncConfig)
// Open the synced realm we just created from the local realm
let syncedRealm = try await Realm(configuration: syncConfig)
// Access the Task objects in the synced realm to see
// that we have all the data we expect
let syncedTasks = syncedRealm.objects(QsTask.self)
var frodoSyncedTasks = syncedTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoSyncedTasks.count, 3)
print("Synced realm opens and contains this many tasks: \(frodoSyncedTasks.count)")
// Add a new task to the synced realm, and see it in the task count
let task4 = QsTask(value: ["name": "Send gift basket to Tom Bombadil", "owner": "Frodo"])
try! syncedRealm.write {
syncedRealm.add(task4)
}
frodoSyncedTasks = syncedTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoSyncedTasks.count, 4)
print("After adding a task, the synced realm contains this many tasks: \(frodoSyncedTasks.count)")
// Open the local realm, and confirm that it still only contains 3 tasks
let openedLocalRealm = try await Realm(configuration: localConfig)
let localTasks = openedLocalRealm.objects(QsTask.self)
let frodoLocalTasks = localTasks.where { $0.owner == "Frodo" }
XCTAssertEqual(frodoLocalTasks.count, 3)
print("Local realm opens and contains this many tasks: \(frodoLocalTasks.count)")
XCTAssertNotEqual(frodoLocalTasks.count, frodoSyncedTasks.count)
/// Populate the local realm with some data that we'll use in the synced realm.
func addExampleData(config: Realm.Configuration) -> Realm {
// Prepare the configuration for the user whose local realm you
// want to convert to a synced realm
let localConfig = config
// Open the local realm, and populate it with some data before returning it
let localRealm = try! Realm(configuration: localConfig)
let task1 = QsTask(value: ["name": "Keep it secret", "owner": "Frodo"])
let task2 = QsTask(value: ["name": "Keep it safe", "owner": "Frodo"])
let task3 = QsTask(value: ["name": "Journey to Bree", "owner": "Frodo"])
try! localRealm.write {
localRealm.add([task1, task2, task3])
}
return localRealm
}
}

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 Atlas App Services 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)")
}

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.

// 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)")

There is no need to manually close a realm in Swift or Objective-C. When a realm goes out of scope and is removed from memory due to ARC, the realm is closed.

Tip
Operating with Low Memory Constraints

Some applications, such as watchOS apps and iOS app extensions, have tight constraints on their memory footprints. To optimize your data model for low-memory environments, open the realm with a subset of classes.

You might define properties whose values are initialized using Realm Database APIs. For example:

class SomeSwiftType {
let persons = try! Realm().objects(Person.self)
// ...
}

If this initialization code runs before you set up your Realm configurations, you might get unexpected behavior. For example, if you set a migration block for the default realm configuration in applicationDidFinishLaunching(), but you create an instance of SomeSwiftType before applicationDidFinishLaunching(), you might be accessing your realm before it has been correctly configured.

To avoid such issues, consider doing one of the following:

  • Defer instantiation of any type that eagerly initializes properties using Realm Database APIs until after your app has completed setting up its realm configurations.
  • Define your properties using Swift's lazy keyword. This allows you to safely instantiate such types at any time during your application's lifecycle, as long as you do not attempt to access your lazy properties until after your app has set up its realm configurations.
  • Only initialize your properties using Realm APIs that explicitly take in user-defined configurations. You can be sure that the configuration values you are using have been set up properly before they are used to open realms.

By default, iOS 8 and above encrypts app files using NSFileProtection whenever the device is locked. If your app attempts to access a realm while the device is locked, you might see the following error:

open() failed: Operation not permitted

To handle this, downgrade the file protection of the folder containing the Realm files. A less strict protection level like NSFileProtectionCompleteUntilFirstUserAuthentication allows file access even when the device is locked.

Tip

If you reduce iOS file encryption, consider using Realm's built-in encryption to secure your data instead.

This example shows how to apply a less strict protection level to the parent directory of the default realm.

let realm = try! Realm()
// Get the realm file's parent directory
let folderPath = realm.configuration.fileURL!.deletingLastPathComponent().path
// Disable file protection for this directory after the user has unlocked the device once
try! FileManager.default.setAttributes([FileAttributeKey.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
ofItemAtPath: folderPath)

Realm may create and delete auxiliary files at any time. Instead of downgrading file protection on the files, apply it to the parent folder. This way, the file protection applies to all relevant files regardless of creation time.

←  Define a Realm Object Schema - Swift SDKRead & Write Data - Swift SDK →
Give Feedback
© 2022 MongoDB, Inc.

About

  • Careers
  • Investor Relations
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2022 MongoDB, Inc.