Docs Menu
Docs Home
/ /
Atlas Device SDKs
/ /

Bundle a Realm File - Swift SDK

On this page

  • Overview
  • Create a Realm File for Bundling
  • Bundle a Realm File in Your Production Application
  • Open a Realm from a Bundled Realm File

Note

Bundle Synchronized Realms

Swift SDK version 10.23.0 introduced the ability to bundle synchronized realms. Before version 10.23.0, you could only bundle local realms.

Realm supports bundling realm files. When you bundle a realm file, you include a database and all of its data in your application download.

This allows users to start applications for the first time with a set of initial data. For synced realms, bundling can avoid a lengthy initial download the first time a user opens your application. Instead, users need only download the synced changes that occurred since you generated the bundled file.

Important

Bundling Synced Realms

If your backend application uses Flexible Sync, users could experience a client reset the first time they open the bundled realm file. This can occur when client maximum offline time is enabled (client maximum offline time is enabled by default). If the bundled realm file was generated more than the number of days specified by the client maximum offline time setting before the user syncs for the first time, the user experiences a client reset.

Applications that perform a client reset download the full state of the realm from the application backend. This negates the advantages of bundling a realm file. To prevent client resets and preserve the advantages of realm file bundling:

  • Avoid using a client maximum offline time in applications that bundle a synchronized realm.

  • If your application does use a client maximum offline time, ensure that your application download always includes a recently synced realm file. Generate a new file each application version, and ensure that no version ever stays current for more than client maximum offline time number of days.

To create and bundle a realm file with your application:

  1. Create a realm file that contains the data you'd like to bundle.

  2. Bundle the realm file in your production application.

  3. In your production application, open the realm from the bundled asset file. For synced realms, you must supply the partition key.

  1. Build a temporary realm app that shares the data model of your application.

  2. Open a realm and add the data you wish to bundle. If using a synchronized realm, allow time for the realm to fully sync.

  3. Use the writeCopy(configuration:) method to copy the realm to a new file:

Tip

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

try await createBundledRealm()
// Opening a realm and accessing it must be done from the same thread.
// Marking this function as `@MainActor` avoids threading-related issues.
@MainActor
func createBundledRealm() async throws {
let app = App(id: YOUR_APP_SERVICES_APP_ID)
// Log in the user whose realm you want to copy for bundling
let seedUser = try await app.login(credentials: Credentials.anonymous)
// Create a configuration to open the seed user's realm
var config = seedUser.configuration(partitionValue: "Partition You Want to Bundle")
config.objectTypes = [Todo.self]
// Open the realm with the seed user's config
let realm = try await Realm(configuration: config, downloadBeforeOpen: .always)
print("Successfully opened realm: \(realm)")
// Verify there is a todo object in the realm whose
// owner's name is "Daenerys". When we open the bundled
// realm later, we should see the same result.
let todos = realm.objects(Todo.self)
let daenerysTodos = todos.where { $0.owner == "Daenerys" }
XCTAssertEqual(daenerysTodos.count, 1)
// Specify an output directory for the bundled realm
// We're using FileManager here for tested code examples,
// but this could be a static directory on your computer.
guard let outputDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
// Append a file name to complete the path
let bundleRealmFilePath = outputDir.appendingPathComponent("seed.realm")
// Update the config file path to the path where you want to save the bundled realm
config.fileURL = bundleRealmFilePath
// Check to see if there is already a realm at the bundled realm file path. If there
// is already a realm there, delete it.
if Realm.fileExists(for: config) {
try Realm.deleteFiles(for: config)
print("Successfully deleted existing realm at path: \(bundleRealmFilePath)")
} else {
print("No file currently exists at path")
}
// Write a copy of the realm at the URL we specified
try realm.writeCopy(configuration: config)
// Verify that we successfully made a copy of the realm
XCTAssert(FileManager.default.fileExists(atPath: bundleRealmFilePath.path))
print("Successfully made a copy of the realm at path: \(bundleRealmFilePath)")
// Verify that opening the realm at the new file URL works.
// Don't download changes, because this can mask a copy
// that does not contain the expected data.
let copiedRealm = try await Realm(configuration: config, downloadBeforeOpen: .never)
print("Successfully opened realm: \(realm)")
// Verify that the copied realm contains the data we expect
let copiedTodos = copiedRealm.objects(Todo.self)
let daenerysCopiedTodos = copiedTodos.where { $0.owner == "Daenerys" }
XCTAssertEqual(daenerysCopiedTodos.count, 1)
print("Copied realm opens and contains this many tasks: \(daenerysCopiedTodos.count)")
}

writeCopy(configuration: ) automatically compacts your realm to the smallest possible size before copying.

Note

Differences Between Synced Realms and Local-only Realms

The above example uses a SyncConfiguration to configure a synchronized realm. To create a copy of a local realm, configure your realm with RealmConfiguration instead.

Now that you have a copy of the realm that contains the initial data, bundle it with your production application. At a broad level, this entails:

  1. Create a new project with the exact same data models as your production app. Open a realm and add the data you wish to bundle. Since realm files are cross-platform, you can do this in a macOS app.

  2. Drag the compacted copy of your realm file to your production app's Xcode Project Navigator.

  3. Go to your app target's Build Phases tab in Xcode. Add the realm file to the Copy Bundle Resources build phase.

  4. At this point, your app can access the bundled realm file. Find its path with Bundle.main.path(forResource:ofType).

You can open the realm at the bundle path directly if the readOnly property is set to true on the Realm.Configuration. If you want to modify the bundled realm, first copy the bundled file to your app's Documents folder with setting seedFilePath with the URL of the bundled Realm on your Configuration.

Tip

See the migration sample app for a complete working app that uses a bundled local realm.

Now that you have a copy of the realm included with your production application, you need to add code to use it. Use the seedFilePath method when configuring your realm to open the realm from the bundled file:

Tip

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

try await openBundledSyncedRealm()
// Opening a realm and accessing it must be done from the same thread.
// Marking this function as `@MainActor` avoids threading-related issues.
@MainActor
func openBundledSyncedRealm() async throws {
let app = App(id: YOUR_APP_SERVICES_APP_ID)
// Log in an app user who will use the bundled realm
let user = try await app.login(credentials: Credentials.anonymous)
// Create a configuration for the app user's realm
// This should use the same partition value as the bundled realm
var newUserConfig = user.configuration(partitionValue: "Partition You Want to Bundle")
newUserConfig.objectTypes = [Todo.self]
// Find the path of the seed.realm file in your project
let realmURL = Bundle.main.url(forResource: "seed", withExtension: ".realm")
print("The bundled realm URL is: \(realmURL)")
// When you use the `seedFilePath` parameter, this copies the
// realm at the specified path for use with the user's config
newUserConfig.seedFilePath = realmURL
// Open the synced realm, downloading any changes before opening it.
// This starts with the existing data in the bundled realm, but checks
// for any updates to the data before opening it in your application.
let realm = try await Realm(configuration: newUserConfig, downloadBeforeOpen: .always)
print("Successfully opened the bundled realm")
// Read and write to the bundled realm as normal
let todos = realm.objects(Todo.self)
// There should be one todo whose owner is Daenerys because that's
// what was in the bundled realm.
var daenerysTodos = todos.where { $0.owner == "Daenerys" }
XCTAssertEqual(daenerysTodos.count, 1)
print("The bundled realm has \(daenerysTodos.count) todos whose owner is Daenerys")
// Write as usual to the realm, and see the object count increment
let todo = Todo(value: ["name": "Banish Ser Jorah", "owner": "Daenerys", "status": "In Progress"])
try realm.write {
realm.add(todo)
}
print("Successfully added a todo to the realm")
daenerysTodos = todos.where { $0.owner == "Daenerys" }
XCTAssertEqual(daenerysTodos.count, 2)
}

Note

Differences Between Synced Realms and Local-only Realms

The above example uses a SyncConfiguration to configure a synchronized realm. To create a copy of a local realm, configure your realm with Realm.Configuration instead.

Back

Delete a Realm