Handling event callbacks
If you're new to Amazon Cognito Sync, use AWS AppSync
It enables user data like app preferences or game state to be synchronized. It also extends these capabilities by allowing multiple users to synchronize and collaborate in real time on shared data.
As an Amazon Cognito Sync developer, you can implement various callbacks to handle different
synchronization events and scenarios. The SyncCallback
interface in the Android
SDK configures notifications about dataset synchronization, including onSuccess()
when a dataset is successfully downloaded, onFailure()
when an exception occurs,
and onConflict()
to resolve conflicts between local and remote data.
In the iOS SDK, you can register for similar notifications like
AWSCognitoDidStartSynchronizeNotification
and set handlers like the
AWSCognitoRecordConflictHandler
for conflict resolution. The JavaScript, Unity,
and Xamarin platforms have analogous callback mechanisms. When you implement these callbacks,
your application can gracefully handle the various synchronization events and scenarios that
can occur when using Amazon Cognito Sync.
Android
SyncCallback Interface
By implementing the SyncCallback
interface, you can receive notifications
on your app about dataset synchronization. Your app can then make active decisions about
deleting local data, merging unauthenticated and authenticated profiles, and resolving sync
conflicts. You should implement the following methods, which are required by the
interface:
-
onSuccess()
-
onFailure()
-
onConflict()
-
onDatasetDeleted()
-
onDatasetsMerged()
Note that, if you don't want to specify all the callbacks, you can also use the class
DefaultSyncCallback
which provides default, empty implementations for all of
them.
onSuccess
The onSuccess()
callback is triggered when a dataset is successfully
downloaded from the sync store.
@Override public void onSuccess(Dataset dataset, List<Record> newRecords) { }
onFailure
onFailure() is called if an exception occurs during synchronization.
@Override public void onFailure(DataStorageException dse) { }
onConflict
Conflicts may arise if the same key has been modified on the local store and in the sync
store. The onConflict()
method handles conflict resolution. If you don't
implement this method, the Amazon Cognito Sync client defaults to using the most recent change.
@Override public boolean onConflict(Dataset dataset, final List<SyncConflict> conflicts) { List<Record> resolvedRecords = new ArrayList<Record>(); for (SyncConflict conflict : conflicts) { /* resolved by taking remote records */ resolvedRecords.add(conflict.resolveWithRemoteRecord()); /* alternately take the local records */ // resolvedRecords.add(conflict.resolveWithLocalRecord()); /* or customer logic, say concatenate strings */ // String newValue = conflict.getRemoteRecord().getValue() // + conflict.getLocalRecord().getValue(); // resolvedRecords.add(conflict.resolveWithValue(newValue); } dataset.resolve(resolvedRecords); // return true so that synchronize() is retried after conflicts are resolved return true; }
onDatasetDeleted
When a dataset is deleted, the Amazon Cognito client uses the SyncCallback
interface
to confirm whether the local cached copy of the dataset should be deleted too. Implement the
onDatasetDeleted()
method to tell the client SDK what to do with the local
data.
@Override public boolean onDatasetDeleted(Dataset dataset, String datasetName) { // return true to delete the local copy of the dataset return true; }
onDatasetMerged
When two previously unconnected identities are linked together, all of their datasets
are merged. Applications are notified of the merge through the
onDatasetsMerged()
method:
@Override public boolean onDatasetsMerged(Dataset dataset, List<String> datasetNames) { // return false to handle Dataset merge outside the synchronization callback return false; }
iOS - Objective-C
Sync Notifications
The Amazon Cognito client will emit a number of NSNotification
events during a
synchronize call. You can register to monitor these notifications via the standard
NSNotificationCenter
:
[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myNotificationHandler:) name:NOTIFICATION_TYPE object:nil];
Amazon Cognito supports five notification types, listed below.
AWSCognitoDidStartSynchronizeNotification
Called when a synchronize operation is starting. The userInfo
will contain
the key dataset which is the name of the dataset being synchronized.
AWSCognitoDidEndSynchronizeNotification
Called when a synchronize operation completes (successfully or otherwise). The
userInfo
will contain the key dataset which is the name of the dataset being
synchronized.
AWSCognitoDidFailToSynchronizeNotification
Called when a synchronize operation fails. The userInfo
will contain the
key dataset which is the name of the dataset being synchronized and the key error which will
contain the error that caused the failure.
AWSCognitoDidChangeRemoteValueNotification
Called when local changes are successfully pushed to Amazon Cognito. The userInfo
will contain the key dataset which is the name of the dataset being synchronized and the key
keys which will contain an NSArray of record keys that were pushed.
AWSCognitoDidChangeLocalValueFromRemoteNotification
Called when a local value changes due to a synchronize operation. The
userInfo
will contain the key dataset which is the name of the dataset being
synchronized and the key keys which will contain an NSArray of record keys that
changed.
Conflict Resolution Handler
During a sync operation, conflicts may arise if the same key has been modified on the local store and in the sync store. If you haven't set a conflict resolution handler, Amazon Cognito defaults to choosing the most recent update.
By implementing and assigning an AWSCognitoRecordConflictHandler you can alter the default conflict resolution. The AWSCognitoConflict input parameter conflict contains an AWSCognitoRecord object for both the local cached data and for the conflicting record in the sync store. Using the AWSCognitoConflict you can resolve the conflict with the local record: [conflict resolveWithLocalRecord], the remote record: [conflict resolveWithRemoteRecord] or a brand new value: [conflict resolveWithValue:value]. Returning nil from this method prevents synchronization from continuing and the conflicts will be presented again the next time the sync process starts.
You can set the conflict resolution handler at the client level:
client.conflictHandler = ^AWSCognitoResolvedConflict* (NSString *datasetName, AWSCognitoConflict *conflict) { // always choose local changes return [conflict resolveWithLocalRecord]; };
Or at the dataset level:
dataset.conflictHandler = ^AWSCognitoResolvedConflict* (NSString *datasetName, AWSCognitoConflict *conflict) { // override and always choose remote changes return [conflict resolveWithRemoteRecord]; };
Dataset Deleted Handler
When a dataset is deleted, the Amazon Cognito client uses the
AWSCognitoDatasetDeletedHandler
to confirm whether the local cached copy of
the dataset should be deleted too. If no AWSCognitoDatasetDeletedHandler
is
implemented, the local data will be purged automatically. Implement an
AWSCognitoDatasetDeletedHandler
if you wish to keep a copy of the local data
before wiping, or to keep the local data.
You can set the dataset deleted handler at the client level:
client.datasetDeletedHandler = ^BOOL (NSString *datasetName) { // make a backup of the data if you choose ... // delete the local data (default behavior) return YES; };
Or at the dataset level:
dataset.datasetDeletedHandler = ^BOOL (NSString *datasetName) { // override default and keep the local data return NO; };
Dataset Merge Handler
When two previously unconnected identities are linked together, all of their datasets
are merged. Applications are notified of the merge through the
DatasetMergeHandler
. The handler will receive the name of the root dataset as
well as an array of dataset names that are marked as merges of the root dataset.
If no DatasetMergeHandler
is implemented, these datasets will be ignored,
but will continue to use up space in the identity's 20 maximum total datasets.
You can set the dataset merge handler at the client level:
client.datasetMergedHandler = ^(NSString *datasetName, NSArray *datasets) { // Blindly delete the datasets for (NSString *name in datasets) { AWSCognitoDataset *merged = [[AWSCognito defaultCognito] openOrCreateDataset:name]; [merged clear]; [merged synchronize]; } };
Or at the dataset level:
dataset.datasetMergedHandler = ^(NSString *datasetName, NSArray *datasets) { // Blindly delete the datasets for (NSString *name in datasets) { AWSCognitoDataset *merged = [[AWSCognito defaultCognito] openOrCreateDataset:name]; // do something with the data if it differs from existing dataset ... // now delete it [merged clear]; [merged synchronize]; } };
iOS - Swift
Sync Notifications
The Amazon Cognito client will emit a number of NSNotification
events during a
synchronize call. You can register to monitor these notifications via the standard
NSNotificationCenter
:
NSNotificationCenter.defaultCenter().addObserver(observer: self, selector: "myNotificationHandler", name:NOTIFICATION_TYPE, object:nil)
Amazon Cognito supports five notification types, listed below.
AWSCognitoDidStartSynchronizeNotification
Called when a synchronize operation is starting. The userInfo
will contain
the key dataset which is the name of the dataset being synchronized.
AWSCognitoDidEndSynchronizeNotification
Called when a synchronize operation completes (successfully or otherwise). The
userInfo
will contain the key dataset which is the name of the dataset being
synchronized.
AWSCognitoDidFailToSynchronizeNotification
Called when a synchronize operation fails. The userInfo
will contain the
key dataset which is the name of the dataset being synchronized and the key error which will
contain the error that caused the failure.
AWSCognitoDidChangeRemoteValueNotification
Called when local changes are successfully pushed to Amazon Cognito. The userInfo
will contain the key dataset which is the name of the dataset being synchronized and the key
keys which will contain an NSArray of record keys that were pushed.
AWSCognitoDidChangeLocalValueFromRemoteNotification
Called when a local value changes due to a synchronize operation. The
userInfo
will contain the key dataset which is the name of the dataset being
synchronized and the key keys which will contain an NSArray of record keys that
changed.
Conflict Resolution Handler
During a sync operation, conflicts may arise if the same key has been modified on the local store and in the sync store. If you haven't set a conflict resolution handler, Amazon Cognito defaults to choosing the most recent update.
By implementing and assigning an AWSCognitoRecordConflictHandler
you can
alter the default conflict resolution. The AWSCognitoConflict
input parameter
conflict contains an AWSCognitoRecord
object for both the local cached data and
for the conflicting record in the sync store. Using the AWSCognitoConflict
you
can resolve the conflict with the local record: [conflict resolveWithLocalRecord], the
remote record: [conflict resolveWithRemoteRecord] or a brand new value: [conflict
resolveWithValue:value]. Returning nil from this method prevents synchronization from
continuing and the conflicts will be presented again the next time the sync process
starts.
You can set the conflict resolution handler at the client level:
client.conflictHandler = { (datasetName: String?, conflict: AWSCognitoConflict?) -> AWSCognitoResolvedConflict? in return conflict.resolveWithLocalRecord() }
Or at the dataset level:
dataset.conflictHandler = { (datasetName: String?, conflict: AWSCognitoConflict?) -> AWSCognitoResolvedConflict? in return conflict.resolveWithLocalRecord() }
Dataset Deleted Handler
When a dataset is deleted, the Amazon Cognito client uses the
AWSCognitoDatasetDeletedHandler
to confirm whether the local cached copy of
the dataset should be deleted too. If no AWSCognitoDatasetDeletedHandler
is
implemented, the local data will be purged automatically. Implement an
AWSCognitoDatasetDeletedHandler
if you wish to keep a copy of the local data
before wiping, or to keep the local data.
You can set the dataset deleted handler at the client level:
client.datasetDeletedHandler = { (datasetName: String!) -> Bool in // make a backup of the data if you choose ... // delete the local data (default behaviour) return true }
Or at the dataset level:
dataset.datasetDeletedHandler = { (datasetName: String!) -> Bool in // make a backup of the data if you choose ... // delete the local data (default behaviour) return true }
Dataset merge handler
When two previously unconnected identities are linked together, all of their datasets
are merged. Applications are notified of the merge through the
DatasetMergeHandler
. The handler will receive the name of the root dataset as
well as an array of dataset names that are marked as merges of the root dataset.
If no DatasetMergeHandler
is implemented, these datasets will be ignored,
but will continue to use up space in the identity's 20 maximum total datasets.
You can set the dataset merge handler at the client level:
client.datasetMergedHandler = { (datasetName: String!, datasets: [AnyObject]!) -> Void in for nameObject in datasets { if let name = nameObject as? String { let merged = AWSCognito.defaultCognito().openOrCreateDataset(name) merged.clear() merged.synchronize() } } }
Or at the dataset level:
dataset.datasetMergedHandler = { (datasetName: String!, datasets: [AnyObject]!) -> Void in for nameObject in datasets { if let name = nameObject as? String { let merged = AWSCognito.defaultCognito().openOrCreateDataset(name) // do something with the data if it differs from existing dataset ... // now delete it merged.clear() merged.synchronize() } } }
JavaScript
Synchronization callbacks
When performing a synchronize() on a dataset, you can optionally specify callbacks to handle each of the following states:
dataset.synchronize({ onSuccess: function(dataset, newRecords) { //... }, onFailure: function(err) { //... }, onConflict: function(dataset, conflicts, callback) { //... }, onDatasetDeleted: function(dataset, datasetName, callback) { //... }, onDatasetMerged: function(dataset, datasetNames, callback) { //... } });
onSuccess()
The onSuccess()
callback is triggered when a dataset is successfully
updated from the sync store. If you do not define a callback, the synchronization will
succeed silently.
onSuccess: function(dataset, newRecords) { console.log('Successfully synchronized ' + newRecords.length + ' new records.'); }
onFailure()
onFailure()
is called if an exception occurs during synchronization. If you
do not define a callback, the synchronization will fail silently.
onFailure: function(err) { console.log('Synchronization failed.'); console.log(err); }
onConflict()
Conflicts may arise if the same key has been modified on the local store and in the sync
store. The onConflict()
method handles conflict resolution. If you don't
implement this method, the synchronization will be aborted when there is a conflict.
onConflict: function(dataset, conflicts, callback) { var resolved = []; for (var i=0; i<conflicts.length; i++) { // Take remote version. resolved.push(conflicts[i].resolveWithRemoteRecord()); // Or... take local version. // resolved.push(conflicts[i].resolveWithLocalRecord()); // Or... use custom logic. // var newValue = conflicts[i].getRemoteRecord().getValue() + conflicts[i].getLocalRecord().getValue(); // resolved.push(conflicts[i].resovleWithValue(newValue); } dataset.resolve(resolved, function() { return callback(true); }); // Or... callback false to stop the synchronization process. // return callback(false); }
onDatasetDeleted()
When a dataset is deleted, the Amazon Cognito client uses the onDatasetDeleted()
callback to decide whether the local cached copy of the dataset should be deleted too. By
default, the dataset will not be deleted.
onDatasetDeleted: function(dataset, datasetName, callback) { // Return true to delete the local copy of the dataset. // Return false to handle deleted datasets outside the synchronization callback. return callback(true); }
onDatasetMerged()
When two previously unconnected identities are linked together, all of their datasets
are merged. Applications are notified of the merge through the
onDatasetsMerged()
callback.
onDatasetMerged: function(dataset, datasetNames, callback) { // Return true to continue the synchronization process. // Return false to handle dataset merges outside the synchronization callback. return callback(false); }
Unity
After you open or create a dataset, you can set different callbacks to it that will be triggered when you use the Synchronize method. This is the way to register your callbacks to them:
dataset.OnSyncSuccess += this.HandleSyncSuccess; dataset.OnSyncFailure += this.HandleSyncFailure; dataset.OnSyncConflict = this.HandleSyncConflict; dataset.OnDatasetMerged = this.HandleDatasetMerged; dataset.OnDatasetDeleted = this.HandleDatasetDeleted;
Note that SyncSuccess
and SyncFailure
use += instead of = so
you can subscribe more than one callback to them.
OnSyncSuccess
The OnSyncSuccess
callback is triggered when a dataset is successfully
updated from the cloud. If you do not define a callback, the synchronization will succeed
silently.
private void HandleSyncSuccess(object sender, SyncSuccessEvent e) { // Continue with your game flow, display the loaded data, etc. }
OnSyncFailure
OnSyncFailure
is called if an exception occurs during synchronization. If
you do not define a callback, the synchronization will fail silently.
private void HandleSyncFailure(object sender, SyncFailureEvent e) { Dataset dataset = sender as Dataset; if (dataset.Metadata != null) { Debug.Log("Sync failed for dataset : " + dataset.Metadata.DatasetName); } else { Debug.Log("Sync failed"); } // Handle the error Debug.LogException(e.Exception); }
OnSyncConflict
Conflicts may arise if the same key has been modified on the local store and in the sync
store. The OnSyncConflict
callback handles conflict resolution. If you don't
implement this method, the synchronization will be aborted when there is a conflict.
private bool HandleSyncConflict(Dataset dataset, List < SyncConflict > conflicts) { if (dataset.Metadata != null) { Debug.LogWarning("Sync conflict " + dataset.Metadata.DatasetName); } else { Debug.LogWarning("Sync conflict"); } List < Amazon.CognitoSync.SyncManager.Record > resolvedRecords = new List < Amazon.CognitoSync.SyncManager.Record > (); foreach(SyncConflict conflictRecord in conflicts) { // SyncManager provides the following default conflict resolution methods: // ResolveWithRemoteRecord - overwrites the local with remote records // ResolveWithLocalRecord - overwrites the remote with local records // ResolveWithValue - to implement your own logic resolvedRecords.Add(conflictRecord.ResolveWithRemoteRecord()); } // resolves the conflicts in local storage dataset.Resolve(resolvedRecords); // on return true the synchronize operation continues where it left, // returning false cancels the synchronize operation return true; }
OnDatasetDeleted
When a dataset is deleted, the Amazon Cognito client uses the OnDatasetDeleted
callback to decide whether the local cached copy of the dataset should be deleted too. By
default, the dataset will not be deleted.
private bool HandleDatasetDeleted(Dataset dataset) { Debug.Log(dataset.Metadata.DatasetName + " Dataset has been deleted"); // Do clean up if necessary // returning true informs the corresponding dataset can be purged in the local storage and return false retains the local dataset return true; }
OnDatasetMerged
When two previously unconnected identities are linked together, all of their datasets
are merged. Applications are notified of the merge through the OnDatasetsMerged
callback.
public bool HandleDatasetMerged(Dataset localDataset, List<string> mergedDatasetNames) { foreach (string name in mergedDatasetNames) { Dataset mergedDataset = syncManager.OpenOrCreateDataset(name); //Lambda function to delete the dataset after fetching it EventHandler<SyncSuccessEvent> lambda; lambda = (object sender, SyncSuccessEvent e) => { ICollection<string> existingValues = localDataset.GetAll().Values; ICollection<string> newValues = mergedDataset.GetAll().Values; //Implement your merge logic here mergedDataset.Delete(); //Delete the dataset locally mergedDataset.OnSyncSuccess -= lambda; //We don't want this callback to be fired again mergedDataset.OnSyncSuccess += (object s2, SyncSuccessEvent e2) => { localDataset.Synchronize(); //Continue the sync operation that was interrupted by the merge }; mergedDataset.Synchronize(); //Synchronize it as deleted, failing to do so will leave us in an inconsistent state }; mergedDataset.OnSyncSuccess += lambda; mergedDataset.Synchronize(); //Asnchronously fetch the dataset } // returning true allows the Synchronize to continue and false stops it return false; }
Xamarin
After you open or create a dataset, you can set different callbacks to it that will be triggered when you use the Synchronize method. This is the way to register your callbacks to them:
dataset.OnSyncSuccess += this.HandleSyncSuccess; dataset.OnSyncFailure += this.HandleSyncFailure; dataset.OnSyncConflict = this.HandleSyncConflict; dataset.OnDatasetMerged = this.HandleDatasetMerged; dataset.OnDatasetDeleted = this.HandleDatasetDeleted;
Note that SyncSuccess
and SyncFailure
use += instead of = so
you can subscribe more than one callback to them.
OnSyncSuccess
The OnSyncSuccess
callback is triggered when a dataset is successfully
updated from the cloud. If you do not define a callback, the synchronization will succeed
silently.
private void HandleSyncSuccess(object sender, SyncSuccessEventArgs e) { // Continue with your game flow, display the loaded data, etc. }
OnSyncFailure
OnSyncFailure
is called if an exception occurs during synchronization. If
you do not define a callback, the synchronization will fail silently.
private void HandleSyncFailure(object sender, SyncFailureEventArgs e) { Dataset dataset = sender as Dataset; if (dataset.Metadata != null) { Console.WriteLine("Sync failed for dataset : " + dataset.Metadata.DatasetName); } else { Console.WriteLine("Sync failed"); } }
OnSyncConflict
Conflicts may arise if the same key has been modified on the local store and in the sync
store. The OnSyncConflict
callback handles conflict resolution. If you don't
implement this method, the synchronization will be aborted when there is a conflict.
private bool HandleSyncConflict(Dataset dataset, List < SyncConflict > conflicts) { if (dataset.Metadata != null) { Console.WriteLine("Sync conflict " + dataset.Metadata.DatasetName); } else { Console.WriteLine("Sync conflict"); } List < Amazon.CognitoSync.SyncManager.Record > resolvedRecords = new List < Amazon.CognitoSync.SyncManager.Record > (); foreach(SyncConflict conflictRecord in conflicts) { // SyncManager provides the following default conflict resolution methods: // ResolveWithRemoteRecord - overwrites the local with remote records // ResolveWithLocalRecord - overwrites the remote with local records // ResolveWithValue - to implement your own logic resolvedRecords.Add(conflictRecord.ResolveWithRemoteRecord()); } // resolves the conflicts in local storage dataset.Resolve(resolvedRecords); // on return true the synchronize operation continues where it left, // returning false cancels the synchronize operation return true; }
OnDatasetDeleted
When a dataset is deleted, the Amazon Cognito client uses the OnDatasetDeleted
callback to decide whether the local cached copy of the dataset should be deleted too. By
default, the dataset will not be deleted.
private bool HandleDatasetDeleted(Dataset dataset) { Console.WriteLine(dataset.Metadata.DatasetName + " Dataset has been deleted"); // Do clean up if necessary // returning true informs the corresponding dataset can be purged in the local storage and return false retains the local dataset return true; }
OnDatasetMerged
When two previously unconnected identities are linked together, all of their datasets
are merged. Applications are notified of the merge through the OnDatasetsMerged
callback.
public bool HandleDatasetMerged(Dataset localDataset, List<string> mergedDatasetNames) { foreach (string name in mergedDatasetNames) { Dataset mergedDataset = syncManager.OpenOrCreateDataset(name); //Implement your merge logic here mergedDataset.OnSyncSuccess += lambda; mergedDataset.SynchronizeAsync(); //Asnchronously fetch the dataset } // returning true allows the Synchronize to continue and false stops it return false; }