Realm ファイルのバンドル - Swift SDK
注意
同期された Realm のバンドル
Swift SDK バージョン10.23.0 同期された Realm をバンドルする機能を導入しました。 バージョン10.23.0より前は、 バンドルできるのはローカル Realm のみです。
Realm は Realm ファイルのバンドルをサポートしています。 Realm ファイルをバンドルする場合、データベースとそのすべてのデータをアプリケーションのダウンロードに含めます。
これにより、ユーザーは初期データセットを使用して初めてアプリケーションを起動できます。 同期済み Realm の場合、バンドルにより、ユーザーが初めてアプリケーションを開くときに長時間かかる初期ダウンロードを回避できます。 代わりに、ユーザーは、バンドル ファイルの生成以降に発生した同期された変更のみをダウンロードする必要があります。
重要
同期された Realm のバンドル
バックエンド アプリケーションで Flexible Syncを使用している場合、バンドルされている Realm ファイルを初めて開くときにクライアントがリセットされる可能性があります。 これは、クライアント最大オフライン時間が有効になっている場合に発生する可能性があります(クライアントの最大オフライン時間はデフォルトで有効になっています)。 ユーザーが最初に同期する前に、バンドルされた Realm ファイルがクライアントの最大オフライン時間設定で指定された日数を超えて生成された場合、ユーザーはクライアントをリセットします。
クライアントリセットを実行するアプリケーションは、アプリケーション バックエンドから Realm の完全な状態をダウンロードします。 これにより、Realm ファイルをバンドルする利点が得られません。 クライアントのリセットを防ぎ、Realm ファイルのバンドルの利点を維持するには、次の手順に従います。
同期された Realm をバンドルするアプリケーションでは、クライアントの最大オフライン時間を使用しないでください。
アプリケーションがクライアントの最大オフライン時間を使用する場合は、アプリケーションのダウンロードに最近同期された Realm ファイルが常に含まれていることを確認してください。 アプリケーション バージョンごとに新しい ファイルを生成し、クライアントの最大オフライン時間数を超えてどのバージョンも最新の状態に維持します。
Overview
Realm ファイルを作成してアプリケーションにバンドルするには、次の手順に従います。
バンドルするデータを含むRealm ファイルを作成します。
本番アプリケーションにRealm ファイルをバンドルします。
本番アプリケーションで、バンドルされた アセット ファイル から Realm を開きます。 同期された Realm の場合は、パーティションキーを指定する必要があります。
バンドル用の Realm ファイルの作成
アプリケーションのデータモデルを共有する一時的な Realm アプリを構築します。
Realm を開き、バンドルするデータを追加します。 同期された Realm を使用する場合は、Realm が完全に同期するまで時間を待ちます。
writeCopy(configuration:)メソッドを使用して、Realm を新しいファイルにコピーします。
Tip
アプリが async/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
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. 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(構成: )は、コピーする前に Realm を可能な限り最小のサイズに自動的に圧縮します。
注意
同期された Realm とローカル専用 Realm の違い
上記の例では、 SyncConfiguration
を使用して同期された Realm を構成しています。 ローカル Realm のコピーを作成するには、代わりにRealmConfiguration
を使用して Realm を構成します。
本番アプリケーションでの Realm ファイルのバンドル
初期データを含む Realm のコピーが用意できたら、それを本番アプリケーションにバンドルします。 大まかに言えば、これには次のことが含まれます。
本番アプリとまったく同じデータモデルで新しいプロジェクトを作成します。 Realm を開き、バンドルするデータを追加します。 Realm ファイルはクロスプラットフォームであるため、macOS アプリでこれを実行できます。
Realm ファイルの圧縮されたコピーを本番アプリの Xcode プロジェクト ナビゲータにドラッグします。
GoBuild PhasesXcode でアプリターゲットの [{0 ] タブに します。Realm ファイルをCopy Bundle Resourcesビルドフェーズに追加します。
この点で 、アプリはバンドルされ邦土ファイルにアクセスできるようになります。 Bundle.main.path(forResource: ofType) でパスを見つけます。
Realm.ConfigurationでreadOnly
プロパティがtrue
に設定されている場合は、バンドル パスで Realm を直接開くことができます。 バンドルされている Realm を変更する場合は、まず構成上のバンドルされた Realm の URL を使用して、 seedFilePath
を設定して、バンドルされた ファイルをアプリのドキュメント フォルダーにコピーします。
Tip
移行サンプル アプリ を見る は、バンドルされたローカル Realm を使用する完全に動作するアプリの ためのものです。
バンドルされた Realm ファイルから Realm を開く
本番アプリケーションに含まれる Realm のコピーが作成できたら、それを使用するためのコードを追加する必要があります。 バンドルされた ファイルからRealmを開くようにRealmを構成するときは、シードファイルパス メソッドを使用します。
Tip
アプリがasync/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
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. 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) }
注意
同期された Realm とローカル専用 Realm の違い
上記の例では、 SyncConfiguration
を使用して同期された Realm を構成しています。 ローカル Realm のコピーを作成するには、代わりにRealm.Configuration
を使用して Realm を構成します。