处理同步错误 — Swift SDK
处理同步错误
在开发使用 Device Sync 的应用程序时,应设置错误处理程序。 此错误处理程序将检测并响应任何失败的同步相关 API 调用。
提示
有关常见Device Sync错误的列表以及如何处理这些错误,请参阅App Services Device Sync文档中的同步错误。
在RLMSyncManager单例上设置错误处理程序。 发生错误时, Swift SDK会使用错误对象和发生错误的RLMSyncSession来调用错误处理程序。
注意
Realm通过域为 RLMSyncErrorDomain 的 NSError 对象表示同步错误。要学习;了解有关错误代码的更多信息,请查看RLMSyncError和RLMSyncAuthError 的定义。
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 };
在SyncManager单例上设置错误处理程序。 在 SyncManager 单例上设置错误处理程序。 发生错误时,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 SDK 可以尝试在客户端重置过程中恢复或丢弃该数据。
有关可能导致客户端重置的原因的更多信息,请Go Atlas App Services文档中的客户端重置。
自动与手动客户端重置
Realm SDK 提供了客户端重置模式,可自动处理大多数客户端重置错误。 客户端自动重置模式将本地 Realm 文件恢复到可同步状态,而无需关闭 Realm 或丢失通知。
除 .manual
之外的所有客户端重置模式均会执行自动客户端重置。 模式之间的差异取决于它们如何处理设备上尚未同步到后端的更改。
选择.recoverUnsyncedChanges
可自动处理大多数客户端重置情况。 这会尝试在客户端重置时恢复未同步的更改。
在某些情况下,您可能希望或需要设置手动客户端重置处理程序。 如果您的应用需要无法自动处理的特定客户端重置逻辑,您可能需要执行此操作。
指定客户端重置模式
在版本 10.32.0 中进行了更改: 添加了客户端恢复,已更改 DiscardLocal 名称
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 中的新增内容。
在客户端重置期间,客户端应用程序可以尝试恢复本地域中尚未同步到后端的数据。 要恢复未同步的更改,必须 在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)") }
有时,客户端重置操作可能无法在丢弃未同步更改模式下完成,例如发生中断性模式更改时。 要处理这种情况,您的应用可以实施手动客户端重置回退。
手动客户端重置模式
当您为.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)") }
提示
如果您使用的是旧版本的 SDK,并且希望查看如何在手动客户端重置中手动恢复更改的示例,请 在 Github上查看此示例。
手动客户端重置回退
如果客户端重置操作无法自动完成(例如发生中断性模式更改时),则客户端重置过程将由手动错误处理程序完成。 在以下任何一种客户端重置模式下都可能会出现这种情况:
.recoverUnsyncedChanges
.recoverOrDiscardUnsyncedChanges
.discardUnsyncedChanges
您可以通过 RLMApp 上的 RLMSyncManager 实例为此回退案例设立错误处理程序。我们建议将手动处理程序视为致命错误恢复情况的工具,在这种情况下,您建议用户更新应用或执行某些其他动作。
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 来手动测试应用程序的客户端重置处理。
当您终止并重新启用 Sync 时,之前使用 Sync 连接的客户端在执行客户端重置之前无法进行连接。 终止同步会从服务器中删除允许客户端同步的元数据。 客户端必须从服务器下载 Realm 的新副本。 服务器向这些客户端发送客户端重置错误。 因此,当您终止同步时,就会trigger客户端重置条件。
要测试客户端重置处理,请执行以下操作:
从客户端应用程序写入数据并等待其同步。
终止并重新启用 Device Sync。
再次运行客户端应用程序。 当应用尝试连接到服务器时,应该会出现客户端重置错误。
警告
当您在客户端应用程序中迭代进行客户端重置处理时,您可能需要反复终止并重新启用 Sync。 终止并重新启用同步会导致所有现有客户端在完成客户端重置之前无法进行同步。 为了避免在生产中出现这种情况,请在开发环境中测试客户端重置处理。