While developing an application that uses Device Sync, you should set an error handler. This error handler will detect and respond to any failed sync-related API calls.
TipFor a list of common Device Sync errors and how to handle them, refer to Sync Errors in the App Services Device Sync documentation.
Set an error handler on the RLMSyncManager singleton. When an error occurs, the Swift SDK calls the error handler with the error object and the RLMSyncSession that the error occurred on.
RLMApp *app = [RLMApp appWithId:YOUR_APP_ID];RLMSyncManager *syncManager = [app syncManager];syncManager.errorHandler = ^(NSError *error, RLMSyncSession *session) { };
Set an error handler on the SyncManager singleton. Set an error handler on the SyncManager singleton. When an error occurs, the Swift SDK calls the error handler with the error object and the SyncSession that the error occurred on.
let app = App(id: YOUR_APP_SERVICES_APP_ID)app.syncManager.errorHandler = { error, session in }
When using Device Sync, a client reset is an error recovery task that your client app must perform when a given synced realm on the server can no longer sync with the client realm. In this case, the client must reset its realm to a state that matches the server in order to restore the ability to sync.
When this occurs, the unsyncable realm on the client may contain data that has not yet synced to the server. Realm SDKs can attempt to recover or discard that data during the client reset process.
For more information about what might cause a client reset to occur, go to Client Resets in the App Services documentation.
The Realm SDKs provide client reset modes that automatically handle most client reset errors. Automatic client reset modes restore your local realm file to a syncable state without closing the realm or missing notifications.
All the client reset modes except .manual
perform an automatic client reset. The differences between the modes are based on how they handle changes on the device that have not yet synced to the backend.
Choose .recoverUnsyncedChanges
to handle most client reset scenarios automatically. This attempts to recover unsynced changes when a client reset occurs.
In some cases, you may want or need to set a manual client reset handler. You may want to do this if your app requires specific client reset logic that can't be handled automatically.
Changed in version 10.32.0: Client recovery added, discardLocal name changed
The Swift SDK provides the option to specify a client reset mode in your SyncConfiguration. This is the .clientResetMode property.
var configuration = user.flexibleSyncConfiguration(clientResetMode: .recoverUnsyncedChanges())
This property takes an enum representing the different client reset modes:
.recoverUnsyncedChanges
.recoverOrDiscardUnsyncedChanges
.discardUnsyncedChanges
.manual
If you do not specify .clientResetMode
in your configuration, the client reset mode defaults to .recoverUnsyncedChanges
.
You can specify a before
and after
block to execute during the automatic client reset process. You might use this to perform recovery logic that is important to your application.
let beforeClientResetBlock: (Realm) -> Void = { before in var recoveryConfig = Realm.Configuration() recoveryConfig.fileURL = myRecoveryPath do { try before.writeCopy(configuration: recoveryConfig) } catch { }}let afterClientResetBlock: (Realm, Realm) -> Void = { before, after in }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)")}
If your app has specific client recovery needs, you can specify the .manual
client reset mode and set a manual client reset handler. You might do this if you have specific custom logic your app must perform during a client reset, or if the client recovery rules do not work for your app.
If your app uses Swift SDK version 10.24.2 or earlier, .clientResetMode
is not an available property on the SyncConfiguration
.
Client Recovery is a feature that is enabled by default when you configure Device Sync. When Client Recovery is enabled, Realm can automatically manage the client reset process in most cases. When you make schema changes:
The client can recover unsynced changes when there are no schema changes, or non-breaking schema changes.
When you make breaking schema changes, the automatic client reset modes fall back to a manual error handler. You can set a manual client reset error handler for this case. Automatic client recovery cannot occur when your app makes breaking schema changes.
For information on breaking vs. non-breaking schema changes, see Breaking vs. Non-Breaking Change Quick Reference.
New in version 10.32.0.
During a client reset, client applications can attempt to recover data in the local realm that has not yet synced to the backend. To recover unsynced changes, Client Recovery must be enabled in your App Services App, as it is by default.
If you want your app to recover changes that have not yet synced, set the .clientResetMode in the SyncConfiguration to one of:
.recoverUnsyncedChanges
: When you choose this mode, the client attempts to recover unsynced changes. Choose this mode when you do not want to fall through to discard unsynced changes.
.recoverOrDiscardUnsyncedChanges
: The client first attempts to recover changes that have not yet synced. If the client cannot recover unsynced data, it falls through to discard unsynced changes but continues to automatically perform the client reset. Choose this mode when you want to enable automatic client recovery to fall back to discard unsynced changes.
var configuration = user.flexibleSyncConfiguration(clientResetMode: .recoverUnsyncedChanges())
There may be times when the client reset operation cannot complete in recover unsynced changes mode, like when there are breaking schema changes or Client Recovery is disabled in the Device Sync configuration. To handle this case, your app can implement a manual client reset fallback.
When Client Recovery is enabled, these rules determine how objects are integrated, including how conflicts are resolved when both the backend and the client make changes to the same object:
Objects created locally that were not synced before client reset are synced.
If an object is deleted on the server, but is modified on the recovering client, the delete takes precedence and the client discards the update.
If an object is deleted on the recovering client, but not the server, then the client applies the server's delete instruction.
In the case of conflicting updates to the same field, the client update is applied.
Changed in version 10.32.0: .discardLocal changed to .discardUnsyncedChanges
The discard unsynced changes client reset mode permanently deletes all local unsynced changes made since the last successful sync. You might use this mode when your app requires client recovery logic that is not consistent with the Device Sync Client Recovery Rules, or when you don't want to recover unsynced data.
Do not use discard unsynced changes mode if your application cannot lose local data that has not yet synced to the backend.
To perform an automatic client reset that discards unsynced changes, set the .clientResetMode in the SyncConfiguration to .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)")}
Note Discard with Recovery
If you'd like to attempt to recover unsynced changes, but discard any changes that cannot be recovered, refer to the .recoverOrDiscardUnsyncedChanges
documentation in Recover Unsynced Changes.
There may be times when the client reset operation cannot complete in discard unsynced changes mode, like when there are breaking schema changes. To handle this case, your app can implement a manual client reset fallback.
When you specify .manual
for .clientResetMode
, you should implement a manual client reset handler.
In .manual
mode, you define your own client reset handler. The handler can take an ErrorReportingBlock
. We recommend using the automatic client recovery modes when possible, and only choosing .manual
mode if the automatic recovery logic is not suitable for your app.
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
If you are using an older version of the SDK, and want to see an example of how you might manually recover changes in a manual client reset, check out this example on GitHub.
If the client reset operation cannot complete automatically, like when there are breaking schema changes, the client reset process falls through to a manual error handler. This may occur in any of these automatic client reset modes:
.recoverUnsyncedChanges
.recoverOrDiscardUnsyncedChanges
.discardUnsyncedChanges
You can set an error handler for this fallback case via the RLMSyncManager instance on your RLMApp. We recommend treating the manual handler as a tool for fatal error recovery situations where you advise users to update the app or perform some other action.
RLMApp *app = [RLMApp appWithId:YOUR_APP_ID];[[app syncManager] setErrorHandler:^(NSError *error, RLMSyncSession *session) { if (error.code == RLMSyncErrorClientResetError) { [RLMSyncSession immediatelyHandleError:[error rlmSync_errorActionToken] syncManager:[app syncManager]]; return; } }];
You can set an error handler for this fallback case via the SyncManager. We recommend treating the manual handler as a tool for fatal error recovery situations where you advise users to update the app or perform some other action.
func handleClientReset() { }do { let app = App(id: APP_ID) let user = try await app.login(credentials: Credentials.anonymous) var config = user.flexibleSyncConfiguration(clientResetMode: .recoverOrDiscardUnsyncedChanges()) 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: () } }} catch { print("Error: \(error.localizedDescription)")}
You can manually test your application's client reset handling by terminating and re-enabling Device Sync.
When you terminate and re-enable Sync, clients that have previously connected with Sync are unable to connect until after they perform a client reset. Terminating Sync deletes the metadata from the server that allows the client to synchronize. The client must download a new copy of the realm from the server. The server sends a client reset error to these clients. So, when you terminate Sync, you trigger the client reset condition.
To test client reset handling:
Write data from a client application and wait for it to synchronize.
Terminate and re-enable Device Sync.
Run the client app again. The app should get a client reset error when it tries to connect to the server.
While you iterate on client reset handling in your client application, you may need to terminate and re-enable Sync repeatedly. Terminating and re-enabling Sync renders all existing clients unable to sync until after completing a client reset. To avoid this in production, test client reset handling in a development environment.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4