同期された Realm へのデータの書き込み - Swift SDK
項目一覧
Overview
Flexible Sync を使用して同期された Realm にデータを書き込む場合、ローカル Realm への書き込みと同じ API を使用できます。 ただし、アプリケーションを開発する際に注意すべき動作の違いがいくつかあります。
同期された Realm に書き込む場合、書き込み操作は次の両方と一致する必要があります。
- 同期サブスクライブ クエリ。
書込み (write) 操作がサブスクライブ内のクエリと一致しない場合、書込み (write) は非致命的な置換書込みエラー(ErrorCompentingWrite)で元に戻ります。
- App Services App での権限。
権限式に一致しないデータを書込み (write) しようとすると、書込み (write) は致命的でない権限拒否エラーで元に戻ります。 クライアントでは、これはエラーとして表示されます(ErrorCompentingWrite)。 サーバーでは、 ロールの書込みフィルターによって書込み (write) が拒否されたかどうかの詳細を確認できます。
アプリの権限設定の詳細については、App Services ドキュメントの「 ロールベースの権限」および「 Device Sync 権限ガイド 」を参照してください。
警告
マルチプロセス同期はサポートされていません
Device Sync では現在、複数のプロセスから同期された Realm を開くことや書込み (write) をサポートしていません。 推奨される代替手段を含む詳細については、「 アプリ拡張機能で同期された Realm に書込みを行わないでください 」を参照してください。
同期するデータの決定
同期された Realm に書き込むデータは、Device Sync 構成、権限、およびRealm を開くときに使用する Flexible Sync サブスクリプション クエリの共通部分です。
このページの例では、次の構成とモデルを使用します。
App Services の構成
Device Sync は、次のクエリ可能なフィールドで構成されています。
_id
(常に含まれる)complexity
ownerId
App Services App には、ユーザーが自分のデータのみを読み書きできるように構成された権限があります。
{ "name": "owner-read-write", "apply_when": {}, "document_filters": { "read": { "ownerId": "%%user.id" }, "write": { "ownerId": "%%user.id" } }, "read": true, "write": true }
クライアント データモデルと構成
このページの例では、次のオブジェクトモデルを使用します。
class Item: Object { true) var _id: ObjectId (primaryKey: var ownerId: String var itemName: String var complexity: Int }
そのオブジェクトモデルを使用すると、同期された Realm 構成は、 complexity
プロパティの値が4
以下のサブスクライブ クエリに一致するオブジェクトを同期します。
let app = App(id: YOUR_APP_ID_HERE) do { let user = try await app.login(credentials: Credentials.anonymous) do { var flexSyncConfig = user.flexibleSyncConfiguration() flexSyncConfig.objectTypes = [Item.self] let realm = try await Realm(configuration: flexSyncConfig) let subscriptions = realm.subscriptions try await subscriptions.update { subscriptions.append( QuerySubscription<Item>(name: "simple-items") { $0.complexity <= 4 }) } print("Successfully opened realm: \(realm)") } catch { print("Failed to open realm: \(error.localizedDescription)") // handle error } } catch { fatalError("Login failed: \(error.localizedDescription)") }
どのデータ同期
サブスクライブ クエリと権限を組み合わせた場合、同期された Realm は次のオブジェクトのみを同期します。
ownerId
はログインユーザーのuser.id
と一致します( 権限による)complexity
プロパティの値は4
(サブスクライブ クエリ)以下です
ownerId
がログインユーザーのuser.id
と一致しない場合、またはcomplexity
プロパティの値が4
より大きい Atlas コレクション内のオブジェクトは、この Realm に同期できません。
同期された Realm への書き込み
Flexible Sync レルムへの書き込みは、大きく次の 2 つのカテゴリのいずれかに分類されます。
正常な書込み: 書込まれたオブジェクトは、クエリ サブスクリプションとユーザーの権限の両方に一致します。 オブジェクトは Realm に正常に書込み (write) され、App Services バックエンドや他のデバイスに正常に同期されます。
書込みの修正 : 書込まれたオブジェクトがサブスクリプション クエリと一致しない場合、またはユーザーが書込みを実行するための十分な権限を持っていない場合、Realm は不正な書込みを元に戻します。
成功した書込み
書き込みがクライアント内の権限とFlexible Sync サブスクライブ クエリの両方に一致する場合、Realm Swift SDK は同期された Realm にオブジェクトを正常に書込むことができます。 このオブジェクトは、デバイスがネットワークに接続している場合に App Services バックエンドと同期します。
// This write falls within the subscription query and complies // with the Device Sync permissions, so this write should succeed. do { let learnRealm = Item() learnRealm.ownerId = user.id learnRealm.itemName = "Learn Realm CRUD stuff" learnRealm.complexity = 3 try realm.write { realm.add(learnRealm) } } catch { print("Failed to write to realm: \(error.localizedDescription)") }
書込みの考慮
最初は成功したように見えた書込みが、実際には不正な書込みである場合もあります。 このような場合、オブジェクトはデータベースに書込み (write) を行いますが、データベースがバックエンドに同期すると、Realm は修復書込み (write) と呼ばれる致命的でないエラー操作で書込みを元に戻します。 次の場合、書込み保証 (write) が発生する可能性があります。
書き込みが クエリ サブスクリプションと一致しません: 書き込まれたオブジェクトはユーザーの権限と一致しますが、クエリ サブスクリプションには一致しません。
書込み (write)は権限と一致しません: 書込まれたオブジェクトはクエリ サブスクリプションと一致しますが、ユーザーの権限と一致しません。
詳しくは、クエリ サブスクリプションの範囲外のデータ、またはユーザーの権限と一致しないデータを書き込むと、次の状況が発生します。
クライアント Realm には「不正」書込みの概念が存在しないため、書込みは最初は Realm が App Services バックエンドで変更セットを解決するまで成功します。
同期時に、サーバーはルールと権限を適用します。 サーバーは、ユーザーには書込み (write) を実行する権限がないと判断します。
サーバーは、「書き込み補正」と呼ばれる元に戻す操作をクライアントに送信します。
クライアントの Realm は不正な書込み (write) 操作を元に戻します。
特定のオブジェクトへの不正な書込み (write) と、それに対応する書込み (write) の間にあるクライアント側の書込み (write) は、いずれもなくなります。
実際には、オブジェクトが Realm に書き込まれ、サーバーがクライアントに補完書込み (write) を送信した後に消えます。
権限拒否エラー、書込み (write) エラー、およびその他の Device Sync エラーのタイプの詳細については、App Services ドキュメントの「同期エラー 」を参照してください。
App Services ログには、 の書き込みエラーが発生する理由に関する詳細情報が記載されています。
書込み (write) エラー情報の置換
バージョン10.37.0の新機能。
補間書込み (write) が発生する理由に関する追加情報をクライアントで取得できます。 Swift SDK は、コードが.writeRejected
である SyncError で補完WriteInfo フィールドを公開します。 この情報には、同期エラー ハンドラーを介してアクセスできます。
このフィールドには、次の機能を提供するRMCompensatedWriteInfoオブジェクトの配列が含まれます。
クライアントが書込み (write) しようとしたオブジェクトの
objectType
特定のオブジェクトの
primaryKey
書き込みエラーを修正するための
reason
この情報は、App Services ログで確認できる情報と同じです。 便宜上とデバッグの目的でクライアント上で公開されます。
例
このエラー ハンドラーは、書込み (write) エラーの修正に関する情報をログに記録する方法の例を示しています。
myApp.syncManager.errorHandler = { syncError, session in if let thisError = syncError as? SyncError { switch thisError.code { // ... additional SyncError.code cases ... case .writeRejected: if let compensatingWriteErrorInfo = thisError.compensatingWriteInfo { for anError in compensatingWriteErrorInfo { print("A write was rejected with a compensating write error") print("The write to object type: \(anError.objectType)") print("With primary key of: \(anError.primaryKey)") print("Was rejected because: \(anError.reason)") } } } } }
上記の例のエラー ハンドラーは、補完書込みエラーが発生したときに次の出力を生成します。
A write was rejected with a compensating write error The write to object type: Optional("Item") With primary key of: objectId(641382026852d9220b2e2bbf) Was rejected because: Optional("write to \"641382026852d9220b2e2bbf\" in table \"Item\" not allowed; object is outside of the current query view")
Optional("Item")
このメッセージの は、Item
Swiftテンプレート アプリ で使用される オブジェクトです。プライマリキーは、クライアントが書込みを試みた特定のオブジェクトの
objectId
です。table \"Item"\
は、このオブジェクトが同期する Atlas コレクションを参照します。この例では
object is outside of the current query view
である理由は、クエリ サブスクリプションがオブジェクトのisComplete
プロパティがtrue
であることを要求するように設定され、クライアントがこのプロパティがfalse
であるオブジェクトを書込み (write) しようとしたためです。
クエリ サブスクリプションに一致しない書込み (write)
サブスクリプション クエリに一致する場合にのみ、 Flexible Sync レルムにオブジェクトを書き込むことができます。 サブスクリプション クエリに一致しない書込み (write) を実行すると、Realm は最初に オブジェクトを書込みますが、その後は補完的な書込み (write) を実行します。 これは、サブスクライブ クエリに一致しない不正な書込み (write) を元に戻す、致命的でない操作です。
実際には、これは書き込みが成功しているように見えても、Realm が App Services バックエンドと同期し、書き込みを実行すると、オブジェクトは「表示されなくなります」。
クエリ サブスクリプションに一致しないオブジェクトを書き込みをクリックする場合は、オブジェクトがクエリ サブスクリプションと一致する別のレルムを開く必要があります。 あるいは、権限やサブスクライブ クエリを強制しない、同期されていない Realm に オブジェクトを書込むこともできます。
コードの例
上記の Flexible Sync レルムの構成では、このオブジェクトを書込み (write) しようとすると、クエリ サブスクリプションは一致しません。
do { let fixTheBug = Item() fixTheBug.ownerId = user.id fixTheBug.itemName = "Fix the bug with the failing method" // The complexity of this item is `7`. This is outside the bounds // of the subscription query, so this write triggers a compensating write. fixTheBug.complexity = 7 try realm.write { realm.add(fixTheBug) } } catch { print("Failed to write to realm: \(error.localizedDescription)") }
クライアント エラー
このシナリオでのクライアント側ログのエラー メッセージは次のとおりです。
Sync: Connection[1]: Session[1]: Received: ERROR "Client attempted a write that is outside of permissions or query filters; it has been reverted" (error_code=231, try_again=true, error_action=Warning)
App Services エラー
このシナリオでの App Services ログのエラー メッセージは次のとおりです。
"FlexibleSync_Item": { "63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\" in table \"FlexibleSync_Item\" not allowed; object is outside of the current query view" }
権限に一致しない書込み
オブジェクトがユーザーのサーバー側書込み権限と一致しない場合は、クライアントに書込み (write) しようとすると、補間書込み (write) エラーがtriggerされることもあります。
クライアントでは、このタイプの書込みはクエリ サブスクリプションに一致しない書込みと同じように動作します。 実際には、これは書き込みが成功しているように見えても、Realm が App Services バックエンドと同期し、書き込みを実行すると、オブジェクトは「表示されなくなります」。
コードの例
上記で詳細に説明されている Device Sync 構成の権限を考慮すると、 ownerId
プロパティがログイン ユーザーのuser.id
と一致しないオブジェクトを書込み (write) しようとすると、正規表現ではありません。
do { let itemWithWrongOwner = Item() // The `ownerId` of this item does not match the `user.id` of the logged-in // user. The user does not have permissions to make this write, so // it triggers a compensating write. itemWithWrongOwner.ownerId = "This string does not match the user.id" itemWithWrongOwner.itemName = "Write code that generates a permission error" itemWithWrongOwner.complexity = 1 try realm.write { realm.add(itemWithWrongOwner) } } catch { print("Failed to write to realm: \(error.localizedDescription)") }
クライアント エラー
このシナリオでのクライアント エラーは、クエリ フィルター外のオブジェクトを書込み (write) しようとした場合と同じです。
Sync: Connection[1]: Session[1]: Received: ERROR "Client attempted a write that is outside of permissions or query filters; it has been reverted" (error_code=231, try_again=true, error_action=Warning)
App Services エラー
App Services ログのエラー メッセージは、クエリ サブスクリプションの問題ではなく、権限の問題であることを判断するのに役立つ追加情報を提供します。 この例では、エラー メッセージは、 オブジェクトがユーザーのロールと一致しないことを示しています。
"FlexibleSync_Item": { "63bdfc40f16be7b1e8c7e4b8": "write to \"63bdfc40f16be7b1e8c7e4b8\" in table \"FlexibleSync_Item\" was denied by write filter in role \"owner-read-write\"" }
パフォーマンス向上のためのグループ書込み
サブスクリプションセットのすべての書込みトランザクション (write transaction) にはパフォーマンス コストがかかります。 セッション中に Realm オブジェクトを複数更新する必要がある場合は、すべての変更が完了するまで編集されたオブジェクトをメモリ内に保持することを検討してください。 これにより、すべての変更ではなく、完全で更新されたオブジェクトのみが Realm に書き込まれるため、同期のパフォーマンスが向上します。
アプリ拡張機能で同期された Realm に書き込まない
共有拡張機能などのアプリ拡張機能を使用するアプリを開発している場合は、その拡張機能内の同期された Realm への書き込みを避けてください。 Device Sync は、最大 1 つのプロセスで同期された Realm を開くことをサポートしています。 実際には、アプリが アプリ拡張機能 で同期された Realm を使用すると、断続的にクラッシュする可能性があります。
複数のプロセスで同期された Realm の起動に関連するクラッシュ
共有拡張機能 またはその他のアプリ拡張機能で同期された Realm を開こうとすると、その Realm がメイン アプリで開かれていない場合、共有拡張機能からの書込み (write) が成功する可能性があります。 ただし、同期された Realm がメインアプリですでに開いている場合、またはバックグラウンドでデータを同期している場合は、 Realm::MultiSyncAgents
に関連するクラッシュが発生する可能性があります。 このシナリオでは、デバイスを再起動する必要がある場合があります。
アプリ 拡張機能で同期された Realm への書き込みの代替手段
アプリ拡張機能から同期された Realm からの読み取りまたは書き込みが必要な場合は、推奨される代替手段がいくつかあります。
オフライン ファースト: ディスク上のデータをメイン アプリとの間で渡す
常に最新の状態: ネットワーク接続を介してバッキング Atlas コレクションと直接通信します
ディスク上のデータを渡す
オフラインファーストの機能がアプリにとって最も重要な考慮事項である場合は、ディスク上のデータをメインアプリに渡したり、メインアプリから渡したりできます。 同期されていない Realm にオブジェクトをコピーし、それを App Group 内のアプリ間で読み取って共有することができます。 または、ディスク上のキューを使用して、メインアプリとの間でデータを送信し、そこから同期された Realm にのみ書き込みを行うこともできます。 その後、デバイスのネットワーク接続に関係なく、アプリ拡張機能との間でいつでも情報を共有できます。
バッキング Atlas コレクションとの直接通信
すべてのデバイスで情報を常に最新にすることがアプリに関する最も重要な考慮事項である場合は、ネットワーク全体でバッキング Atlas コレクションとの間でデータを直接読み書きできます。 ニーズに応じて、これらのツールのいずれかを使用して Atlas と直接通信することをお勧めします。
Realm Swift SDK MongoClientを使用した Atlas のクエリ
App Services Functionにデータを渡す
Data APIを使用した HTTPS 呼び出しの実行
すると、ネットワーク接続を持つすべてのデバイスは、上記の オプションのようにユーザーがメインアプリを開くのを待つことなく、常に最新情報を取得します。
このオプションでは、アプリ拡張機能を使用するときにユーザーのデバイスがネットワーク接続を持っている必要があります。 フォールバックとして、ネットワーク接続を確認できます。 次に、ユーザーのデバイスがネットワークに接続できない場合は、上記の ディスク上の オプションを使用します。