同期された Realm の設定とオープン - Swift SDK
項目一覧
同期された Realm
Realm を構成して、それぞれがデータのローカル コピーを持つ多数のデバイス間でデータを自動的に同期できます。 同期された Realm はローカル専用の Realm とは異なる構成を使用するため、同期プロセスを処理するには Atlas App Services バックエンド が必要です。
アプリケーションは、オフラインでも、ローカルで同期された Realm オブジェクトをいつでも作成、変更、削除できます。 ネットワーク接続が利用可能になるたびに、Realm SDK はアプリケーション サーバーへの接続を開き、他のクライアントとの間での変更を同期します。 Atlas Device Sync プロトコルとサーバー側の運用変換では、特定の変更がオフラインで発生したり、正しい順序で受信されなかった場合でも、Realm の完全に同期されたすべてのインスタンスが完全に同じデータを参照することが保証されます。
同期された Realm と同期されていない Realm
同期された Realm と同期されていないローカル Realm とはいくつかの違いがあります。
同期された Realm ではバックエンドの App Services Appと変更が同期されようとしますが、同期されていない Realm では変更が同期されません。
同期された Realm には認証済みユーザーがアクセスできますが、同期されていない Realm にはユーザーや認証の概念がありません。
同期済み Realm を使用すると、Realm を開く前に更新をダウンロードするためのダウンロード動作を指定できます。 ただし、Realm を開く前にダウンロードする変更を要求するには、ユーザーがオンラインである必要があります。 同期されていない Realm は、いつでもオフラインで使用できます。
同期されていない Realm から同期された Realm に、またはその逆にデータをコピーすることはできますが、同期されていない Realm を同期することはできません。 同期されていない Realm を同期された Realm に変換するには、「ローカル専用アプリの同期可能なアプリへの移行 」で説明されているプロセスに従います。
同期されていない Realm を構成して開く方法の詳細については、次の Realm の構成とオープン - Swift SDK を参照してください。
同期された Realm を開く
同期された Realm を開くための通常のフローは、次のとおりです。
同期構成の作成。
ユーザーの同期された Realm を 構成で起動します。
認証時に、ユーザー認証情報をデバイス上の sync_metadata.realm
ファイルにキャッシュします。
認証後に同期された Realm を開くと、ログイン フローをバイパスし、すでに作成した同じ同期構成を使用して同期された Realm を開くことができGo 。
キャッシュされた認証情報を使用すると、次のことが可能になります。
デバイス上のデータと同期された Realm をすぐに開きます。 このメソッドはオフラインでもオンラインでも使用できます。
アプリから変更をダウンロードした後、同期された Realm を開きます。 これには、ユーザーがアクティブなインターネット接続を持っている必要があります。
Flexible Sync を使用する場合、同期された Realm を開くにはflexibleSyncConfiguration()
を使用します。
Tip
アプリがasync/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
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. 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 }
重要
Flexible Sync にはサブスクリプションが必要
アプリが双方向の標準 Device Sync を使用している場合、少なくとも 1 つのサブスクリプションを追加するまで Flexible Sync レルムは使用できません。 サブスクリプションを追加する方法については、「サブスクリプションの追加 」を参照してください。
これは、 Data Ingestには適用されません。 AsymmetricObject
のサブスクリプションは作成できません。
メモリ内で同期された Realm を開く
バージョン 10.46.0 の新機能。
同期されたデータベースは完全にメモリ内で開くことができます。この場合、 .realm
ファイルやそれに関連する予備ファイルは作成されません。 代わりに、SDK は Realm が開いている間にオブジェクトをメモリに保存し、すべての インスタンスが閉じられるとすぐにそれらを破棄します。
メモリ内で同期された Realm を開くには、Realm 構成のinMemoryIdentifierプロパティを string 識別子に設定します。
// 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
別の同期ユーザーとして同期された Realm を開く
バージョン 10.23.0 の新機能。
別の同期ユーザーとして同期された Realm を開き、書込み (write)メソッドを使用して、新しいユーザーの同期構成で使用する同期された Realm のコピーを作成できます。 以下の例では、別の同期構成で使用できる既存のすべてのデータを含む同期された Realm のコピーを作成します。
新しい同期ユーザーの構成の Realm をコピーした後、そのユーザーの同期された Realm としてコピーを開くことができます。
注意
同じタイプの同期のみ
このメソッドは、別のパーティションベースの同期ユーザーのパーティションベースの同期構成、または別の Flexible Sync ユーザーの Flexible Sync 構成のコピーのみをサポートします。 このメソッドを使用して、 パーティションベースの同期 Realm と Flexible Sync レルムの間で、またはその逆に変換することはできません。
Tip
アプリがasync/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
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. 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) }
Swift 同時実行機能で同期された Realm を開く
Swift 同時実行機能を使用して、同期された Realm をアクター分離された Realm として開くことができます。
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) }
アクター分離された Realm の操作の詳細については、「 アクターでRealm を使用する - Swift SDK 」を参照してください。
開く前に変更をダウンロード
バージョン 10.15.0 の新機能。
Swift SDK で同期された Realm を開くときに、Realm を開く前にdownloadBeforeOpen
パラメータを渡して、アプリから変更セットをダウンロードするかどうかを指定できます。 このパラメータは、 OpenBehavior
列挙型のケースを受け入れます。
never
: デバイス上の Realm をすぐに開きます。 ユーザーがインターネットを利用している場合、バックグラウンドで変更をダウンロードしますが、Realm を開くのはブロックしません。always
: Realm を開くたびに変更を確認します。 ユーザーはアクティブなインターネット接続を持っている必要があります。once
: Realm を初めて開く前にデータをダウンロードするが、その後を開く際には変更をダウンロードせずに開きます。 これにより、Realm に初期データを入力できますが、後続のオープンではオフラインからの機能が有効になります。
Tip
アプリがasync/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
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
同期された Realm をオフラインで開く
Realm アプリケーションはユーザーを認証する際、ユーザーの認証情報をキャッシュします。 既存のユーザー認証情報を確認して、ログイン フローをバイパスし、キャッシュされたユーザーにアクセスできます。 これを使用して、Realm をオフラインで開きます。
注意
最初のログインにはネットワーク接続が必要です
ユーザーがアプリにサインアップしたり、クライアントに既存のアカウントを使用して初めてログインする場合、クライアントにはネットワーク接続が必要です。 キャッシュされたユーザー認証情報を確認すると、ユーザーが以前にオンライン時にログインしたことがある場合にのみ、オフラインで Realm を開くことができます。
Realm を開く前にクライアントアプリで変更をダウンロードする必要がない場合にのみ、同期された Realm をオフラインで開くことができalways
。
Tip
アプリがasync/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
// 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)")