同期エラーの処理 - Swift SDK
項目一覧
同期エラーの処理
Device Sync を使用するアプリケーションを開発する際には、エラー ハンドラーを設定する必要があります。 このエラー ハンドラーは、失敗した同期関連の API 呼び出しを検出し、応答します。
Tip
一般的な Device Sync エラーのリストとその処理方法については、App Services Device Sync ドキュメントの「 Sync エラー 」を参照してください。
RMSyncManager シングルオンにエラーハンドラーを設定します。 エラーが発生すると、Swift SDK は、エラー オブジェクトとエラーが発生したRMSyncSessionを使用してエラー ハンドラーを呼び出します。
注意
Realm は、ドメインが RMSyncErrorDomain である DNSError オブジェクトを通じて同期エラーを表します。エラー コードの詳細については、 RMSyncErrorとRMSyncAuthError の定義を確認してください。
RLMApp *app = [RLMApp appWithId:YOUR_APP_ID]; // Access the sync manager for the app RLMSyncManager *syncManager = [app syncManager]; syncManager.errorHandler = ^(NSError *error, RLMSyncSession *session) { // handle error };
Sync Manager のみにエラー ハンドラーを設定します。 Sync Manager のみにエラー ハンドラーを設定します。 エラーが発生すると、Swift SDK は、エラー オブジェクトとエラーが発生したSyncSessionを使用してエラー ハンドラーを呼び出します。
注意
Realm の SyncError は Swift のエラープロトコル に準拠しています
let app = App(id: YOUR_APP_SERVICES_APP_ID) app.syncManager.errorHandler = { error, session in // handle error }
クライアント リセット
Device Syncを使用する場合、クライアントリセットは、サーバー上の特定の同期された Realm がクライアント Realm と同期できなくなった場合にクライアントアプリが実行する必要があるエラー回復タスクです。 この場合、同期機能を復元するには、クライアントが Realm をサーバーと一致する状態にリセットする必要があります。
この状況が発生すると、クライアント上の同期できない Realm には、サーバーにまだ同期されていないデータが含まれている可能性があります。 Realm SDK は、クライアントのリセット プロセス中にそのデータの回復または破棄を試行できます。
クライアント リセットが発生する可能性のある原因について詳しくGo は、Atlas App Services ドキュメントの「 クライアントのリセット 」に してください。
自動クライアントリセットと手動クライアントリセット
Realm SDK は、ほとんどのクライアント リセット エラーを自動的に処理するクライアント リセット モードを提供します。 自動クライアント リセット モードでは、Realm を閉じたり通知が欠落したりすることなく、ローカル Realm ファイルが同期可能な状態に復元されます。
.manual
を除くすべてのクライアント リセット モードで、自動クライアント リセットが実行されます。 モード間の違いは、バックエンドにまだ同期されていないデバイス上の変更をどのように処理するかに基づいています。
ほとんどのクライアント リセット シナリオを自動的に処理するには、 .recoverUnsyncedChanges
を選択します。 これは、クライアントがリセットしたときに同期されていない変更を回復しようとします。
場合によっては、クライアント リセット ハンドラーを手動で設定する必要があるか、または設定する必要がある場合があります。 自動的には処理できない特定のクライアント リセット ロジックがアプリで必要とされる場合は、これを実行することをお勧めします。
クライアントリセットモードの指定
バージョン10.32.0での変更: クライアントリカバリを追加し、破棄ローカルの名前を変更しました
Swift SDK には、 SyncConfigurationでクライアント リセット モードを指定するオプションが用意されています。 これは.clientResetMode プロパティ。
// Specify the clientResetMode when you create the SyncConfiguration. // If you do not specify, this defaults to `.recoverUnsyncedChanges` mode. var configuration = user.flexibleSyncConfiguration(clientResetMode: .recoverUnsyncedChanges())
このプロパティは、さまざまなクライアント リセット モードを表す列挙型を受け取ります。
.recoverUnsyncedChanges
.recoverOrDiscardUnsyncedChanges
.discardUnsyncedChanges
.manual
構成で.clientResetMode
を指定しない場合、クライアント リセット モードはデフォルトで.recoverUnsyncedChanges
になります。
before
after
自動クライアント リセット プロセス中に実行する ブロックと ブロックを指定できます。これを使用して、アプリケーションにとって重要なリカバリ ロジックを実行することがあります。
// A block called after a client reset error is detected, but before the // client recovery process is executed. // This block could be used for any custom logic, reporting, debugging etc. // This is one example, but your usage may vary. let beforeClientResetBlock: (Realm) -> Void = { before in var recoveryConfig = Realm.Configuration() recoveryConfig.fileURL = myRecoveryPath do { try before.writeCopy(configuration: recoveryConfig) // The copied realm could be used later for recovery, debugging, reporting, etc. } catch { // handle error } } // A block called after the client recovery process has executed. // This block could be used for custom recovery, reporting, debugging etc. // This is one example, but your usage may vary. let afterClientResetBlock: (Realm, Realm) -> Void = { before, after in // let res = after.objects(myClass.self) // if (res.filter("primaryKey == %@", object.primaryKey).first != nil) { // // ...custom recovery logic... // } else { // // ...custom recovery logic... // } // } } do { let app = App(id: YOUR_APP_SERVICES_APP_ID) let user = try await app.login(credentials: Credentials.anonymous) var configuration = user.flexibleSyncConfiguration(clientResetMode: .recoverOrDiscardUnsyncedChanges( beforeReset: beforeClientResetBlock, afterReset: afterClientResetBlock)) } catch { print("Error logging in user: \(error.localizedDescription)") }
アプリに特定のクライアント回復が必要な場合は、 .manual
クライアント リセット モードを指定し、手動のクライアント リセット ハンドラーを設定できます。 クライアント リセット中にアプリで実行する必要がある特定のカスタム ロジックがある場合、またはクライアント回復ルールがアプリで機能しない場合は、この操作を行うことがあります。
注意
アプリで Swift SDK バージョン 10.24.2 またはそれ以前のバージョンが使用されている場合、 .clientResetMode
はSyncConfiguration
で使用できないプロパティではありません。
スキーマの変更の処理
クライアントリカバリは、 Device Sync を構成するとデフォルトで有効になる機能です。 クライアントリカバリが有効になっている場合、Realm はほとんどの場合にクライアントのリセットプロセスを自動的に管理できます。 スキーマを変更する際:
クライアントは、スキーマ変更がない場合、またはスキーマの重大でない変更がない場合に、同期されていない変更を回復できます。
スキーマの重大な変更を行うと、自動クライアント リセット モードは手動エラー ハンドラーにフォールバックします。 この場合は、手動のクライアント リセット エラー ハンドラーを設定できます。 アプリがスキーマの重大な変更を行った場合は、自動クライアント回復は実行できません。
スキーマの重大な変更と重大でない変更の詳細については、「 重大な変更と重大でない変更のクイック リファレンス 」を参照してください。
同期されていない変更の回復
バージョン10.32.0の新機能。
クライアントのリセット中に、クライアント アプリケーションは、バックエンドにまだ同期されていないローカル Realm 内のデータの回復を試行できます。 同期されていない変更を回復する には、 App Services App でデフォルトで有効になっている クライアントリカバリ を有効にする必要があります。
まだ同期されていない変更をアプリで回復するには、 SyncConfigurationの.clientResetModeを次のいずれかに設定します。
.recoverUnsyncedChanges
: このモードを選択すると、クライアントは同期されていない変更の回復を試みます。 同期されていない変更を破棄するために失敗したくない場合は、このモードを選択します。.recoverOrDiscardUnsyncedChanges
: クライアントは最初に、まだ同期されていない変更の回復を試みます。 クライアントが同期されていないデータを復元できない場合、同期されていない変更を破棄しますが、クライアントのリセットの自動実行は続行されます。 同期されていない変更を破棄するためにバックアップする自動クライアントリカバリを有効にする場合は、このモードを選択します。
// Specify the clientResetMode when you create the SyncConfiguration. // If you do not specify, this defaults to `.recoverUnsyncedChanges` mode. var configuration = user.flexibleSyncConfiguration(clientResetMode: .recoverUnsyncedChanges())
スキーマの重大な変更が発生した場合や、Device Sync 構成でクライアントリカバリが無効になっている場合など、同期されていない変更モードでクライアントのリセット操作が完了しない場合があります。 この場合を処理するために、アプリは手動のクライアントリセットフォールバックを実装できます。
クライアント回復ルール
クライアントリカバリが有効になっている場合、バックエンドとクライアントの両方が同じオブジェクトに変更を加えた場合に競合が解決される方法など、オブジェクトの統合方法がこれらのルールによって決まります。
クライアントがリセットされる前に同期されなかったローカルで作成されたオブジェクトが同期されます。
オブジェクトがサーバー上で削除されたが、復元されたクライアントで変更された場合は、削除が優先され、クライアントは更新を破棄します。
リカバリ クライアントでオブジェクトが削除されたが、サーバーでは削除された場合、クライアントはサーバーの削除指示を適用します。
同じフィールドへの更新が競合する場合は、クライアント更新が適用されます。
同期されていない変更の破棄
バージョン 10.32.0 での変更: .discardLocal .discardUnsyncedchanges に変更
同期されていない変更を破棄するクライアント リセット モードでは、最後の同期が成功した以降に行われたすべてのローカルの同期されていない変更が永続的に削除されます。 Device Syncクライアント回復ルールと整合性のないクライアント回復ロジックがアプリで必要な場合、または同期されていないデータを回復したくない場合は、このモードを使用できます。
バックエンドにまだ同期されていないローカル データをアプリケーションが失うことができない場合は、同期されていない変更モードを使用しないでください。
同期されていない変更を破棄する自動クライアントリセットを実行するには、 SyncConfigurationの.clientResetModeを.discardUnsyncedChanges
に設定します。
do { let app = App(id: APP_ID) let user = try await app.login(credentials: Credentials.anonymous) var config = user.flexibleSyncConfiguration(clientResetMode: .discardUnsyncedChanges()) } catch { print("Error logging in user: \(error.localizedDescription)") }
注意
リカバリを使用して破棄
同期されていない変更の復元を試み、復元できない変更を破棄する場合は、同期されていない変更の復元に関する.recoverOrDiscardUnsyncedChanges
のドキュメントを参照してください。
スキーマの重大な変更が発生した場合など、非同期変更の破棄モードではクライアントのリセット操作が完了しない場合があります。 この場合を処理するために、アプリは手動のクライアントリセットフォールバックを実装できます。
手動クライアントリセットモード
.clientResetMode
に.manual
を指定する場合は、手動クライアント リセット ハンドラーを実装する必要があります。
.manual
モードでは、独自のクライアント リセット ハンドラーを定義します。 ハンドラーはErrorReportingBlock
を指定できます。 可能な場合は自動クライアント回復モードを使用し、自動回復ロジックがアプリに適していない場合にのみ.manual
モードを選択することをお勧めします。
do { let app = App(id: APP_ID) let user = try await app.login(credentials: Credentials.anonymous) var config = user.flexibleSyncConfiguration(clientResetMode: .manual()) } catch { print("Error logging in user: \(error.localizedDescription)") }
Tip
古いバージョンの SDK を使用しており、手動クライアント リセットで変更を手動で回復する方法の例が必要な場合は 、 Githubでこの例を確認してください。
手動クライアントリセットフォールバック
スキーマの重大な変更が発生した場合など、クライアントのリセット操作が自動的に完了しない場合、クライアントのリセットプロセスは手動のエラーハンドラーに降格します。 これは、次のいずれかの自動クライアント リセット モードで発生する可能性があります。
.recoverUnsyncedChanges
.recoverOrDiscardUnsyncedChanges
.discardUnsyncedChanges
このフォールバック ケース用のエラー ハンドラーは、 RMApp の RMSyncManager インスタンスを介して設定できます。アプリをアップデートしたり、その他のアクションを実行するようユーザーに提案する場合、致命的なエラー回復ツールとして手動ハンドラーを使うことをお勧めします。
RLMApp *app = [RLMApp appWithId:YOUR_APP_ID]; [[app syncManager] setErrorHandler:^(NSError *error, RLMSyncSession *session) { if (error.code == RLMSyncErrorClientResetError) { // TODO: Invalidate all open realm instances // TODO: Restore the local changes backed up at [error rlmSync_clientResetBackedUpRealmPath] [RLMSyncSession immediatelyHandleError:[error rlmSync_errorActionToken] syncManager:[app syncManager]]; return; } // Handle other errors... }];
SyncManagerを使用して、このフォールバックケースのエラー ハンドラーを設定できます。 アプリをアップデートしたり、その他のアクションを実行するようユーザーに提案する場合、致命的なエラー回復ツールとして手動ハンドラーを使うことをお勧めします。
func handleClientReset() { // Report the client reset error to the user, or do some custom logic. } do { let app = App(id: APP_ID) let user = try await app.login(credentials: Credentials.anonymous) var config = user.flexibleSyncConfiguration(clientResetMode: .recoverOrDiscardUnsyncedChanges()) // If client recovery fails, app.syncManager.errorHandler = { error, session in guard let syncError = error as? SyncError else { fatalError("Unexpected error type passed to sync error handler! \(error)") } switch syncError.code { case .clientResetError: if let (path, clientResetToken) = syncError.clientResetInfo() { handleClientReset() SyncSession.immediatelyHandleError(clientResetToken, syncManager: app.syncManager) } default: // Handle other errors... () } } } catch { print("Error: \(error.localizedDescription)") }
クライアント リセット処理をテストします
Device Sync を終了して再度有効にすることで、アプリケーションのクライアント リセット処理を手動でテストできます。
同期を終了して再度有効にすると、以前に同期に接続したことのあるクライアントは、クライアント リセットを実行するまで接続できなくなります。 同期を終了すると、クライアントが同期できるようにするメタデータがサーバーから削除されます。 クライアントは、サーバーから Realm の新しいコピーをダウンロードする必要があります。 サーバーはこれらのクライアントにクライアント リセット エラーを送信します。 したがって、同期を終了すると、クライアントのリセット条件がtriggerされます。
クライアント リセット処理をテストするには:
クライアント アプリケーションからデータを書き込み、同期されるまで待ちます。
Device Sync を終了して再度有効にします。
クライアント アプリを再度実行します。 アプリは、サーバーに接続しようとすると、クライアント リセット エラーを取得します。
警告
クライアント アプリケーションでクライアント リセット処理を反復処理している間に、同期を繰り返し終了して再度有効にする必要がある場合があります。 同期を終了して再度有効にすると、既存のすべてのクライアントは、クライアントのリセットが完了するまで同期できなくなります。 本番環境でこれを回避するには、開発環境でクライアントのリセット処理をテストします。