Manage Sync Sessions - Swift SDK
On this page
Opening a synced realm starts a SyncSession for that realm. The Realm Swift SDK provides methods to manually pause and resume a sync session.
Sync Connection Behavior
New in version 10.41.0.
In Realm Swift SDK version 10.41.0 and later, App Services defaults to
sharing a single connection to the server for all opened synced realms.
This is a change from earlier versions where opening more than one synced
realm opened additional connections to the server. The connection to the
server is independent of the SyncSession
, and is based on the App
Services user.
You can change this behavior from the App client configuration.
Check the Network Connection
Tip
Realm's offline-first design means that you generally don't
need to check the current network connection state. That said, the
connectionState
property is available if your app calls for some
indication of connection state.
To check the connection state, you can read the synced realm's RLMSyncSession instance's connectionState property directly.
This property is KVO-compliant, so you can observe changes using KVO. The following example demonstrates how to implement an observer class:
@interface MySyncSessionObserver: NSObject @end @implementation MySyncSessionObserver - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (![object isKindOfClass:RLMSyncSession.class]) { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; return; } if (![keyPath isEqualToString:@"connectionState"]) { // Not interested in observing this keypath return; } RLMSyncSession *syncSession = (RLMSyncSession *)object; RLMSyncConnectionState connectionState = [syncSession connectionState]; switch (connectionState) { case RLMSyncConnectionStateConnecting: NSLog(@"Connecting..."); break; case RLMSyncConnectionStateConnected: NSLog(@"Connected"); break; case RLMSyncConnectionStateDisconnected: NSLog(@"Disconnected"); break; } } @end
You can then attach an observer instance to the RLMSyncSession object. Be sure to remove the observer when finished.
// Observe connectionState for changes using KVO MySyncSessionObserver *observer = [[MySyncSessionObserver alloc] init]; [syncSession addObserver:observer forKeyPath:@"connectionState" options:NSKeyValueObservingOptionInitial context:nil]; // Later, when done... [syncSession removeObserver:observer forKeyPath:@"connectionState" context:nil];
To check the connection state, you can read the synced realm's SyncSession instance's connectionState property directly.
This property is KVO-compliant, so you can observe changes using KVO or even Combine.
// Observe connectionState for changes using KVO let observer = syncSession.observe(\.connectionState, options: [.initial]) { (syncSession, change) in switch syncSession.connectionState { case .connecting: print("Connecting...") case .connected: print("Connected") case .disconnected: print("Disconnected") default: break } } // Observe using Combine let cancellable = syncSession.publisher(for: \.connectionState) .sink { connectionState in switch connectionState { case .connecting: print("Connecting...") case .connected: print("Connected") case .disconnected: print("Disconnected") default: break } }
Suspend or Resume a Sync Session
You can suspend and resume the sync session on the realm. Pausing a sync session only suspends that realm's sync session. If you have more than one open realm, suspend does not affect the sync sessions for other realms.
You can suspend or resume a sync session using the RLMSyncSession instance of a synced realm.
RLMRealm *syncedRealm = [RLMRealm realmWithConfiguration:configuration error:nil]; RLMSyncSession *syncSession = [syncedRealm syncSession]; // Suspend synchronization [syncSession suspend]; // Later, resume synchronization [syncSession resume];
You can suspend or resume a sync session using the SyncSession instance of a synced realm.
let syncSession = syncedRealm.syncSession! // Suspend synchronization syncSession.suspend() // Later, resume synchronization syncSession.resume()
When to Pause a Sync Session
For most applications, there is no need to manually pause and resume a sync session. However, there are a few circumstances under which you may want to pause or suspend a sync session:
You only want to sync after the user takes a specific action
You only want to sync during a certain time of the day
You don't want to attempt to sync when there is poor network connectivity
You want to explicitly force a sync session to connect
In the case of poor network connectivity, continually trying to establish a network connection can drain the user's device battery.
The case of explicitly forcing a sync session to connect is most commonly related to being offline for some time. The sync client attempts to connect, and upon failure, goes into exponential backoff. After being offline for a long time, the client may not immediately reconnect. Pausing and resuming the sync session explicitly forces the connection.
When you do pause a sync session, keep these things in mind:
If the client may be offline longer than the client maximum offline time, the client will be unable to resume syncing and must perform a client reset.
Pausing a sync session pauses it in both directions. Changes that your app makes on the device do not sync with the backend, and changes to the data in the backend or on other devices do not sync to the device. There is no way to pause only uploads or pause only downloads.
Do not pause a sync session if you want a client to permanently stop syncing with the backend. To permanently stop syncing, copy the contents of the synced realm into a non-synced realm, and use the non-synced realm in the client.
Do not pause sync to stop syncing for indefinite time periods or time ranges in months and years. The functionality is not designed or tested for these use cases. You could encounter a range of issues when using it this way.
Wait for Changes to Upload or Download
New in version 10.45.0.
To wait for all changes to upload or download from your synced realm, call realm.syncSession?.wait(for: ).
This method takes a ProgressDirection argument to specify whether to track upload or download progress.
You can use these methods with Swift's async/await syntax, or with the callback syntax. The callback version, realm.syncSession?.wait(for:queue:block:), can take a queue to dispatch the callback onto, and a block to invoke when waiting is complete.
// Wait to download all pending changes from Atlas try await realm.syncSession?.wait(for: .download) // Add data locally try realm.write { realm.create(Task.self, value: [ "taskName": "Review proposal", "assignee": "Emma", "completed": false, "progressMinutes": 0, "dueDate": date ]) } // Wait for local changes to be uploaded to Atlas try await realm.syncSession?.wait(for: .upload)
// Wait to download all pending changes from Atlas realm.syncSession?.wait(for: .download, block: { _ in // You can provide a block to execute // after waiting for download to complete }) // Add data locally do { try realm.write { realm.create(Task.self, value: [ "taskName": "Review proposal", "assignee": "Emma", "completed": false, "progressMinutes": 0, "dueDate": date ]) } } catch { print("There was an error writing to realm: \(error.localizedDescription)") } // Wait for local changes to be uploaded to Atlas realm.syncSession?.wait(for: .upload, block: { _ in // You can provide a block to execute after // waiting for upload to complete })
Check Upload & Download Progress for a Sync Session
Changed in version 10.50.0: transferredBytes
and transferrableBytes
deprecated in favor of progressEstimate
You can check upload and download progress by registering a token that provides
a progressEstimate
for a given upload or download direction and work scope.
You can set a ProgressMode
to determine the work scope: either observe
indefinitely or unregister the block after the current work item has completed.
The progressEstimate
value provided by the token is a double whose value
ranges from 0.0
to 1.0
. At 1.0
, the upload or download is complete.
You can use this progressEstimate
to display a progress indicator or
estimated data transfer percentage.
Changed in version 10.50.0: addProgressNotificationForDirection
deprecated in favor of addSyncProgressNotificationForDirection
You can add a progress notification using the synced realm's RLMSyncSession instance's [--addSyncProgressNotificationForDirection:mode:block:] method.
This method returns a token that you should retain until you wish to stop observing upload or download progress. Note that if you keep the token in a local variable, observation will stop when the local variable goes out of scope.
RLMSyncSession *syncSession = [syncedRealm syncSession]; RLMProgressNotificationToken *token = [syncSession addSyncProgressNotificationForDirection:RLMSyncProgressDirectionUpload mode:RLMSyncProgressModeForCurrentlyOutstandingWork block:^(RLMSyncProgress syncProgress) { NSLog(@"Uploaded %fB", (double)syncProgress.progressEstimate); }]; // Upload something [syncedRealm transactionWithBlock:^{ [syncedRealm addObject:[[Task alloc] init]]; }];
You can add a progress notification using the synced realm's SyncSession instance's addProgressNotification(for:mode:block:) method.
This method returns a token that you should retain until you wish to stop observing upload or download progress. Note that if you keep the token in a local variable, observation will stop when the local variable goes out of scope.
let syncSession = realm.syncSession! let token = syncSession.addProgressNotification( for: .upload, mode: .forCurrentlyOutstandingWork) { (progress) in let progressEstimate = progress.progressEstimate let transferPercent = progressEstimate * 100 print("Uploaded (\(transferPercent)%)") }
Manually Reconnect All Sync Sessions
New in version 10.44.0.
Realm automatically detects when a device regains connectivity after being offline and attempts to reconnect using an incremental backoff strategy. For example, on Apple platforms, Realm listens for network change notifications and automatically triggers a reconnect immediately after receiving one.
In Swift SDK version 10.44.0 and later, you can choose to manually trigger a
reconnect attempt with SyncSession.reconnect()
instead of waiting for the duration of the incremental backoff. This is
useful if you have a more accurate understanding of
the network conditions and don't want to rely on Realm's automatic
reconnect detection.
let syncSession = realm.syncSession! // Work with the realm. When you need to force the sync session to reconnect... syncSession.reconnect()
When you call this method, the SDK forces all sync sessions to attempt to reconnect immediately. This resets any timers used for incremental backoff.
Calling this method does not guarantee the device can reconnect. If the SDK gets a fatal error, or if the device is already connected or is trying to connect, calling this method has no effect.
Important
Cannot Reconnect Within Socket Read Timeout Duration
Realm has an internal default socket read timeout of 2 minutes, where
Realm will time out if a read operation does not receive any data
within a 2-minute window. If you call SyncSession.reconnect()
within that window, the Swift SDK does not attempt to reconnect.