Configure & Open a Realm - Swift SDK
On this page
- Synced Realms vs. Non-Synced Realms
- Convert Between Synced and Non-Synced Realms
- Open a Realm Without Sync
- Open a Default Realm or Realm at a File URL
- Open an In-Memory Realm
- Open a Synced Realm as a Non-Synced Realm
- Open a Synced Realm
- Open a Synced Realm for Partition-Based Sync
- Open a Synced Realm for Flexible Sync
- Open a Synced Realm as a Different Sync User
- Open Non-Synced Realm as a Synced Realm
- Download Changes Before Open
- Open a Synced Realm Offline
- Close a Realm
- Handle Errors
- Provide a Subset of Classes to a Realm
- Initialize Properties Using Realm APIs
- Use Realm When the Device Is Locked
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 vs. Non-Synced Realms
Synced realms differ from non-synced local Realm Database in a few ways:
- Synced realms attempt to sync changes with your backend Realm 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.
Convert Between Synced and Non-Synced Realms
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:
Open a Realm Without Sync
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
Open a Default Realm or Realm at a File URL
Open an In-Memory Realm
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.
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.
Open a Synced Realm as a Non-Synced Realm
New in version 10.23.0.
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 backend Realm app, 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 backend Realm application.
func testConvertSyncToLocal() async throws { let app = App(id: YOUR_REALM_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) }
Open a Synced Realm
The typical flow for opening a synced realm involves:
- Authenticating the user.
- Creating a sync configuration.
- 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.
Open a Synced Realm for Partition-Based Sync
Initialize a synced realm with a sync configuration. This enables you to specify a partition value whose data should sync to the realm.
Open a Synced Realm for Flexible Sync
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)
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.
Open a Synced Realm as a Different Sync User
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_REALM_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) }
Open Non-Synced Realm as a Synced Realm
New in version 10.23.0.
If you want a non-synced realm to start syncing with other devices and your backend Realm application, 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 backend Realm application.
func testConvertLocalToSync() async throws { let app = App(id: YOUR_REALM_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 } }
Download Changes Before Open
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)") }
Open a Synced Realm Offline
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.
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_REALM_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)")
Close a 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.
Handle Errors
Provide a Subset of Classes to a Realm
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.
Initialize Properties Using Realm APIs
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 yourlazy
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.
Use Realm When the Device Is Locked
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.
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.