This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

What is rSDK?

Resilient SDK (rSDK) is a new major release of the @platform with several changes to make apps using the SDK more resilient and fault tolerant. This guide is intended to help app developers with the use of rSDK.

What’s new?

rSDK offers three major changes:

  1. Simpler abstractions
  2. Better feedback on the status of an operation
  3. Resilience from network failures

High Level Architecture

high level architecture

Key Abstractions

AtClientManager

Factory class responsible for giving the instances of AtClient and other services for a given @sign.

AtClient

AtClient should be used to perform Create, Read, Update or Delete (CRUD) operations on the secondary server.

NotificationService

NotificationService should be used to send and receive notifications from the secondary server.

SyncService

SyncService should be used to keep local and cloud secondary data in sync.

Sync

Sync in the @protocol is a process to pull or push data between local and cloud secondary servers to make sure that the data is in sync.

For example, if an app creates a key called phone@bob and saves it to the local secondary, the Sync process will push that to the cloud secondary server and make it available to be consumed by another @app on another device.

Sync triggers

Sync process is triggered in two ways:

  1. App sync
  2. System sync

App sync

Apps on @protocol can request for an invocation of a Sync process by calling the “sync” method on “SyncService”. Calling “sync” would not immediately trigger the Sync process, rather it is taken as a request that needs to be fulfilled later. For example when a Sync process is requested an app could be offline but SyncService still accepts the request through the “sync” method call and fulfills it when the conditions are right for the data transfer.

System sync

SDK initiates a Sync process without any apps requesting when SDK detects that the local and the cloud secondaries are out of sync.

Please look at SyncService Dart code docs for more details on the usage.

Sync trigger conditions

When a sync request is submitted, SyncService waits for the following conditions to be fulfilled for the Sync process to be triggered:

  1. Number of sync requests should be greater than 3
  2. Time difference between the first sync request and now is greater than 5 seconds

Note: These internals are subject to change in future versions of SDK

1 - What are the changes?

Refernce documentation for rSDK changes.

Get instances of AtClient and other services.

  1. To initialize AtClient Instance call setCurrentAtSign method on AtClientManager.getInstance().
  • setCurrentAtSign accepts the following arguments: currentAtSign, namespace and the preferences.

    AtClientManager.getInstance().setCurrentAtSign('@alice', 'wavi', <preferences>);
    
  1. The AtClientManger Instance has getter atClient which returns an instance of AtClient.

    AtClient atClient = atClientManager.atClient;
    
  2. The AtClientManager instance has a late initialized variable notificationService which is for accessing notification service methods.

    NotificationService notificationService = atClientManager.notificationService;
    
  3. The AtClientManager instance has a late initialized variable syncService which is for invoking the sync.

    SyncService syncService = atClientManager.syncService;
    

Note: Above code should be executed every time when the @sign is switched to get the right instances representing the new @sign.

Sending and receiving notifications.

  1. Get the AtClientManager instance initially.

  2. Access the notificationService variable using atClientManager instance.

  3. Listen to notifications via callback and no filter. Ideally you don’t want to do this.

    /// AtClientManager instance.
    AtClientManager atClientManager = AtClientManager.getInstance();
    
    /// NotificationService variable.
    NotificationService notificationService = atClientManager.notificationService;
    
    /// Listen to notifications.
    notificationService.subscribe().listen((notification) {
    _notificationCallback(notification);
    });
    
  4. Listen to notifications via callback and filter notification key by regex. You can also come up with regexes that match other types of keys. Ex: ‘wavi | buzz’ or alternatively multiple listeners can also be registered.

    notificationService.subscribe(regex: '.wavi').listen((notification) {
    _notificationCallback(notification);
    });
    
  5. Send notification. Await variant.

    /// AtKey
    AtKey phoneKey = AtKey()
        ..key = 'phone'
        ..sharedWith = '@bob🛠'
        ..sharedBy = '@alice🛠';
    
    /// AtKey's Value (String)
    String atValue = '+1 100 200 300';
    
    /// Update the value and capture the notification result.
    NotificationResult notificationResponse = await notificationService
                .notify(NotificationParams.forUpdate(phoneKey, value: atValue));
    
  6. Validating if a notification failed in the await variant.

    if(notificationResponse.notificationStatusEnum == NotificationStatusEnum.undelivered) {
        // Do something on notification error.
        print(notificationResponse.atClientException);
    } else{
        // Do something on successful delivery response.
        print('Successfully delivered notification.');
    }
    
  7. The other notifications are as follows -

    /// Delete notification
    NotificationResult notificationResponse = await notificationService.notify(NotificationParams.forDelete(phoneKey));
    
    /// Text notify
    NotificationResult notificationResponse = await notificationService.notify(NotificationParams.forText('phone', '@bob🛠'));
    
  8. Send notification using Callback.

    notificationService.notify(
          NotificationParams.forUpdate(atKey, value: atValue),
          onDone: _onSuccessCallback,
          onError: _onErrorCallback,
          );
    
    void _onSuccessCallback(notificationResult){
        // Do something on successful delivery response.
        print(notificationResult);
    }
    
    void _onErrorCallback(notificationResult){
        // Do something on notification error
        print(notificationResponse.atClientException);
    }
    

Syncing the data.

  1. The atClientManger instance has getter atClient which returns an instance of AtClient.

    final AtClient atClient = atClientManager.atClient;
    
  2. CRUD operations

    atClient.put(<params>)
    atClient.delete(<params>)
    
  3. Syncing the data. Apps no longer have to use SyncStrategy or isDedicated flag or manually call sync. All sync requests will be internally kept in a queue and synced to the server at periodic time interval (approx. 15 seconds). If remote server is updated from some other device, then those changes will be also synced at periodic intervals.

    syncService = atClientManager.syncService;
    
    • Optionally, Register to onDone callbacks to get SyncResult when run asynchronously.
    syncService.sync(onDone: _onSuccessCallback);
    void _onSuccessCallback(syncResult){
        print(syncResult);
    }
    
  4. Optionally, call setOnDone for global onDone callback. Call this method to set the Global onDone callback. This method will be called when a sync is completed. When a specific onDone function is passed to the sync function, then the specific onDone is called.

    syncService.setOnDone(onDone: _onSuccessCallback);
    

2 - How to migrate?

Documentation referring how to migrate your project to latest rSDK.

If you have gone through the rSDK changes, migrating your app will be easy. If you are ready to migrate your project to latest rSDK changes, then you are good to go.

Get instance of atClient.

  • AtClientImpl has been replace with AtClientManager instance.

  • createClient method has been removed and replaced with setCurrentAtSign.

  • atClient instance can be obtained through atClientManager instance.

/// **SDK 2.X**

await AtClientImpl.createClient('@alice', 'wavi',  <preference>);
var atClient = await (AtClientImpl.getClient(atsign));
/// **SDK 3.X**

var atClientManager = await AtClientManager.getInstance().setCurrentAtSign('@alice', 'wavi', <preference>);
final atClient = atClientManager.atClient;

Starting listening to notifications.

  • startMonitor method has been removed. With rSDK you must subscribe and listen to atClient instance’s NotificationService.

  • subscribe method takes a optional parameter regex to filter the notifications.

  • Decoding the response on callback function is not required.

/// **SDK 2.X**

await atClient!.startMonitor(<private_key>, _notificationCallBack, regex: 'atmosphere');

void _notificationCallBack(var response) {
    response = response.replaceFirst('notification:', '');
    var responseJson = jsonDecode(response);
    var notificationKey = responseJson['key'];
    var fromAtSign = responseJson['from'];
    // ....... REST CODE .......
}
/// **SDK 3.X**

atClient!.notificationService.subscribe(regex: 'wavi').listen(_notificationCallBack);

void _notificationCallBack(AtNotification atNotification) {
    var notificationKey = atNotification.key;
    var fromAtSign = atNotification.from;
    // ....... REST CODE .......
}

Send notifications.

  • To send notification, you must use AtClientManager instance’s NotificationService to access notify method.

  • notify method takes a positional parameter NotificationParams.

  • The NotificationParams has all the methods depending to the operation you do.

    • forUpdate() - To send update notification.
    • forDelete() - To send delete notification.
    • forText() - To send a text message to another atSign.
/// **SDK 2.X**

await AtClientImpl.createClient('@alice', 'wavi', <preference>);
atClient = await (AtClientImpl.getClient('@alice')); 
AtKey atKey = AtKey()..key = 'phone'
                   ..sharedWith = '@bob'
                   ..sharedBy = '@alice';
String atValue = '+1 445 446 7879';
atClient.notify(atKey, atValue, OperationEnum.update);
/// **SDK 3.X**

AtClientManager atClientManager = await AtClientManager.getInstance()
    	.setCurrentAtSign(‘@alice, 'wavi', <preference>);
AtClient atClient = atClientManager.atClient;
AtKey atKey = AtKey()..key = 'phone'
                   ..sharedWith = '@bob'
                   ..sharedBy = '@alice';
String atValue = '+1 445 446 7879';
NotificationResult result  = await atClientManager.notificationService
                        .notify(NotificationParams.forUpdate(atKey, value: atValue));

Check whether local and remote server are in sync.

  • Instead of using SyncManager, use SyncService to access isInSync method.

  • You must use AtClientManager instance’s SyncService to access isInSync method.

/// **SDK 2.X**

final syncManager = atClient.getSyncManager();
bool isInSync = await syncManager.isInSync();
/// **SDK 3.X**

final syncService = atClientManager.syncService;
bool isInSync = await syncService.isInSync();

Calling on demand sync.

  • This may not be needed by all apps with new SDK changes. sync is performed automatically on any update/delete operation on atClient.
/// **SDK 2.X**

final syncManager = atClient.getSyncManager();
await syncManager.sync();
/// **SDK 3.X**

final syncService = atClientManager.syncService;
syncService.sync(); 

3 - What's new with rSDK?

Documentation referring What is new with rSDK and how to use it.

Network availability callback service.

Apps can subscribe to the network availability callback service to know when the network goes off and on. Some of the services offered by SDK is subscribing to this service internally.

final ConnectivityListener connectivityListener = ConnectivityListener();
connectivityListener.subscribe().listen((isConnected) {
if(isConnected) {
print('connected');
} else {
print('disconnected');
}
});

// call this when app is closed or you no longer need the subscription
connectivityListener.unSubscribe() 

Response objects.

Notification Response Object.

class AtNotification {
late String id;
late String key;
late String from;
late String to;
late int epochMillis;
String? value;
String? operation;
}

Sync Response Object

class SyncResult {
SyncStatus syncStatus = SyncStatus.not_started;
AtClientException? atClientException;
DateTime? lastSyncedOn;
bool dataChange = true;
}
enum SyncStatus { not_started, success, failure }

4 - What's deprecated in rSDK?

Documentation referring What are all the deprecations happened in rSDK.

isDedicated flag

Apps no longer have to use the isDedicated flag on at_client methods. Sync will be called on a separate connection with the new SDK implementation. App developers can remove references to the isDedicated flag.

SyncStrategy

Apps no longer have to set SyncStrategy in the preferences. Sync will be called automatically after any update or delete operation on the at_client; Refer to the Sync section below for more details.

notify method

The method notify from AtClient is deprecated. Use AtClientManager.notificationService instance to call notify method instead. This method will be taking a positional argument of type NotificationParams.

Usage :

/// Get [AtClientManager] instance.
AtClientManager atClientManager = AtClientManager.getInstance();

/// From the [AtClientManager] instance access the `notificationService` instance.
/// Using `NotificationService` instance call the `notify` method. 
atClientManager.notificationService.notify(
    /// With respect to the operation type, 
    /// the notification will be sent to the server.
    NotificationParams.forUpdate(
        atKey,
        value: value,
    ),
);

getSyncManager method

The method getSyncManager used to get the instance of SynaManager is deprecated in favor of SyncService; Can be able to access from AtClientManager instance.

Usage :

/// Get [AtClientManager] instance.
AtClientManager atClientManager = AtClientManager.getInstance();

/// Get [SyncService] instance.
/// Use this sync service instance wherever 
/// you want to perform sync operations.
SyncService _syncServcie = atClientManager.syncService;

startMonitor method

The method startMonitor() from AtClient has been deprecated in favor of subscribe() from NotificationService. This method gives a back stream of notifications from the server to the subscribing client. Optionally pass a regex to filter notification keys matching the regex.

Usage :

/// Get [AtClientManager] instance.
AtClientManager atClientManager = AtClientManager.getInstance();

/// Get [NotificationService] instance from [AtClientManager].
NotificationService notificationService = atClientManager.notificationService;

/// Subscribe to notificationService.
/// Pass optional regex to filter notification 
/// keys matching the regex as namespace.
notificationService.subscribe(regex: '.myatapp');

5 - What are the best practices?

  • Do not cache instances of AtClient or any other services in the app code. Always make use of “AtClientManager” to get the right instance.

  • Subscribe to only specific notifications.

    • Secondary servers send certain system-level notifications to the SDK. All of these system notifications will not be of interest to the apps.
  • Use response parsers where the app has to deal with raw responses from the server.

    • Secondary servers send responses in various forms for various verbs. Ideally, app developers should not have to know the specific response format. Response parsers are made available in SDK to parse these responses and give back simple beans that the app can operate on.
  • Build feedback loops into the apps when an operation fails

    • For example, if @X wants to notify @Y, the operation might fail for many reasons. The SDK would let the caller know the reason for the failure. It is better to act on the failure in the app code. It could be as simple as logging it.
  • Do not write any services to optimize the SDK code. There is a good chance that we are already working on it. Make sure that you raise an enhancement request for any such requirement.

  • Use sync with regex with caution. It is advised to use without regex unless you are sure about it.