Offline

Working with the offline mode.

Introduction to AppClient Offline Mode

API Express supports the offline mode and that is realized by using App Client. Here is how it works:

  • Initially, your app will be in online mode (state = online) no matter whether the internet on the device was enabled or not.
  • When the client goes offline (state = offline), all change-requests (POST, PUT, DELETE) to the server are going to be queued and stored to the cache.
  • When the client goes online (state= synchronizing), the queue of deferred change-requests will be handled, and one after one, all the requests will be executed.
  • In case of a synchronization conflict (state = sync-failed), the error will be stored.

When a device is connected to the internet, App Client synchronizes with the server and sends all offline changes made by the user. The mobile app automatically detects a connection state and switches App Client to an online/offline mode when the connection state has changed. In the AppClientSettings service there is an option called handleNetworkState that is activated by default. You can disable this if needed.

There are few functions in the App Client library that help to handle switching between online and offline modes, and also trigger the synchronization or reset data that failed to sync.
Initially, App Client is not included in your Appery.io app. It will be added to the app as AppClientInit.js (it will be listed under the JavaScript folder), during services generation via the API Express Generator extension.

This diagram will give you an overview of how the online-offline functionality works.

533

Online-offline diagram.

There are two types of generated REST API services:

  1. Services that return data (operations find and read).
  2. Services that modify data (operations create, update, delete).

Every API Express model has a predefined structure (predefined fields and their types). When an app is online and performs find or get operation, App Client saves the returned data in app storage (IndexedDB).

When an app is in the offline mode, App Client returns data of the get and find operations from storage (cache).

Operations create, update and delete modify data in storage, and the sequence of operations performed is placed into history.

Operations create, update and delete modify data in storage (cache).

After switching from offline to online mode synchronization process will start running.

This means that the app needs to synchronize data with the server after a disconnect. In order to do that:

  • App replicates changes (made in the app when the app was offline) with the server.
  • Server replicates changes (made on the server when the app was offline) with the app.

Replication means intelligently copying data from one location to another.

Network State Detection

AppClient can detect internet connection state change automatically.

The handleNetworkState option can be used to enable/disable automatic internet state detection during initialization. Automatic internet state detection is activated by default.

When handleNetworkState is enabled (value is true) then AppClient checks the internet connection state during initialization and goes to the appropriate mode.

After initialization is finished, AppClient starts watching for the internet connection state changes all the time.

If handleNetworkState is disabled (value is false) then the app logic should watch for internet state change and switch App Client to the appropriate state manually.

Using on AppClient Test Page

The Handle network state option is available on the AppClient test page added on the SDK Settings panel. Use this option before AppClient is initialized.

Using in App

If API Express services are used in an app then the Services folder will have the AppClientSettings file. This is the place where settings for AppClient are stored.

If an app should be notified about AppClient state change, then an app can subscribe to the AppClient statechange event.

Using in Ionic App

The following code will detect state change in an Ionic app:

//first get initialized AppClient instance via AppClientGetInstance service
this.Apperyio.getService("AppClientGetInstance").then(
   service => {
       service.execute({}).subscribe(
           (appClientInstance: any) => {
               //subscribe to AppClient state updates
               appClientInstance.on("statechange", (newState) => {
                   //doing something depends on new AppClient state
                   console.log(`AppClient state has changed. New state: ${newState}`);
               });
               console.log("ok");
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

The following code will detect state change in a jQuery Mobile app:

//first get initialized AppClient instance
mssdk().then(function(AppClientInstance){
    //subscribe to AppClient state updates
    AppClientInstance.on("statechange", function(currentState){
        //doing something depends on new AppClient state
    });
});

Clearing Model Cache

Get DAO (Data Access Object) object for a specific model from AppClient. The cache can be cleared using the clearCache function of the DAO object.

Using in Ionic App

Clearing cache in an Ionic app:

//first get initialized AppClient instance via AppClientGetInstance service
this.Apperyio.getService("AppClientGetInstance").then(
   service => {
       service.execute({}).subscribe(
           (appClientInstance: any) => {
               const exampleModelDAOObject = appClientInstance.dao("messages");
               exampleModelDAOObject.clearCache().then(()=>{
                   console.log("success");
               }, (err: any)=>{
                   console.error(err);
               });
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

Clearing cache in a jQuery Mobile app:

var exampleModelDAOObject = AppClient.dao("exampleModel");
exampleModelDAOObject.clearCache().then(function(){
    //success
}, function(error){
    //error
});

User Auto Login

When the auto-login feature is enabled (autoLogin option in AppClientSettings) then during AppClient initialization, if a user was logged in before, a login will be automatically executed. For example, an app has a login page and when it starts, the app needs to know if a user is logged in or not. In this case isUserLoggedIn function of AppClient can be used.

Using in Ionic App

Auto user login in an Ionic app:

//first get initialized AppClient instance via AppClientGetInstance service
this.Apperyio.getService("AppClientGetInstance").then(
   service => {
       service.execute({}).subscribe(
           (appClientInstance: any) => {
               appClientInstance.isUserLoggedIn().then((result) => {
                   if (!result) {
                       //stay on login screen
                   } else {
                       //user is logged, skip login
                   }
               });
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

Auto user login in a jQuery Mobile app:

mssdk().then(function(AppClientInstance){
    AppClientInstance.isUserLoggedIn().then(function(result){
        if(!result){
            //stay on login screen
        }else{
            //user is logged, skip login
        }
    });
});

goOffline

AppClient can change the mode to offline from the online state only. During any offline work all of the calls to the server will be deferred (persisted in localStorage/indexedDB in a queue).

Using in Ionic App

For Ionic projects, the following works:

//change AppClient state to offline via the AppClientGoOffline service
this.Apperyio.getService("AppClientGoOffline").then(
   service => {
       service.execute({}).subscribe(
           (res: any) => {
               console.log('success');
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

mssdk().then(function(sdk) {sdk.goOffline();});

goOnline

AppClient can switch the app mode to online only from the offline state. During the process of going online, the deferred server calls stored in the cache are to be performed during this synchronization process – state = synchronising.
Every cached operation will be executed one-by-one. The Synchronisation process will stop in case of an error (state = sync-failed).

Using in Ionic App

For Ionic projects, the following works:

//change AppClient state to online via the AppClientGoOnline service
this.Apperyio.getService("AppClientGoOnline").then(
   service => {
       service.execute({}).subscribe(
           (res: any) => {
               console.log('success');
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

mssdk().then(function(sdk) {sdk.goOnline();});

retrySync

The retrySync function can be executed to retry the synchronization process and will work only when state = sync-failed.

Using in Ionic App

For Ionic projects, the following works:

this.Apperyio.getService("AppClientRetrySync").then(
   service => {
       service.execute({}).subscribe(
           (res: any) => {
               console.log('success');
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

mssdk().then(function(sdk) {sdk.retrySync();});

resetFailedSync

resetFailedSync will erase all of the operations that were not performed successfully. The operations that were executed successfully will not be affected; it will only work when state = sync-failed.
After this operation execution, switching the app mode to online will be performed.

Using in Ionic App

For Ionic projects, the following works:

this.Apperyio.getService("AppClientResetFailedSync").then(
   service => {
       service.execute({}).subscribe(
           (res: any) => {
               console.log('success');
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

mssdk().then(function(sdk) {sdk.resetFailedSync();});

getState

Use the getState method to determine the current app state.

Using in Ionic App

For Ionic projects, the following works:

this.Apperyio.getService("AppClientGetState").then(
   service => {
       service.execute({}).subscribe(
           (res: any) => {
               console.log('success');
               console.log(`AppClient state: ${res.state}`);
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

mssdk().then(function(sdk) {console.log(sdk.state);});

revertLocalChanges

Use the revertLocalChanges method to revert all local changes made in offline mode without clearing the cached data. It can also be useful in case of a failed synchronization or conflicted objects.

Using in Ionic App

For Ionic projects, the following works:

//first get initialized AppClient instance via AppClientGetInstance service
this.Apperyio.getService("AppClientGetInstance").then(
   service => {
       service.execute({}).subscribe(
           (appClientInstance: any) => {
               appClientInstance.revertLocalChanges().then(() => {
                   //Reverted
                   console.log('success');
               }, (error) => {
                   //Error
                   console.error(error);
               });
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

invokeAppClientMethod("revertLocalChanges").then(function(){
 //Reverted
},function(){
 //Error
});

getDeferredActions

By using the getDeferredAction method you will return the iterator, which you can use further to iterate through the array items. History in this case is the type of array. This array contains deferred actions that will be executed only if the app goes online, so that is why they are deferred.

Using in Ionic App

For Ionic projects, the following works:

//first get initialized AppClient instance via AppClientGetInstance service
this.Apperyio.getService("AppClientGetInstance").then(
   service => {
       service.execute({}).subscribe(
           (appClientInstance: any) => {
               appClientInstance.getDeferredActions().then((historyIterator: any) => {
                   console.log('success');
                   let historyIteratorResult = historyIterator.next();
                   while (!historyIteratorResult.done) {
                       let historyItem = historyIteratorResult.value;
                       //work with historyItem
                       historyIteratorResult = historyIterator.next();
                   }
               }, (error) => {
                   //Error
                   console.error(error);
               });
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

AppClient.getDeferredActions().then(function(historyIterator){
   var historyIteratorResult = historyIterator.next();
   while(!historyIteratorResult.done){
      var historyItem = historyIteratorResult.value;
      //work with historyItem
      historyIteratorResult = historyIterator.next();
   }
}, function(error){
 
});

A retrieved object (called historyIterator in current example) has the following methods:

  • next()– returns JSON object of the stored action with the following structure:
{
   done: false,
   value: historyItem //history item object
}
  • done– end of history items indicator, if true then there are no more history items.
  • value– actual history item value has the following structure:
{
   modelName: name, //model name
   operation: operation, //operation on model
   content:{
      data: operationData, //operation data
      revertedData: revertedData, //data before operation
   },
   httpErrorResponse: error // exists if synchronization process failed on current history item
}
  • remove() – removes current action from the history.

saveDeferredActions

If you modified the history of saved deferred actions, you should save the changes by using the saveDeferredActions method.

Using in Ionic App

For Ionic projects, the following works:

//first get initialized AppClient instance via AppClientGetInstance service
this.Apperyio.getService("AppClientGetInstance").then(
   service => {
       service.execute({}).subscribe(
           (appClientInstance: any) => {
               appClientInstance.saveDeferredActions().then(() => {
                   //history saved
                   console.log('success');
               }, (error) => {
                   //Error
                   console.error(error);
               });
           },
           (err: any) => {
               console.error(err);
           }
       )
   }
);

Using in jQuery Mobile App

For jQM projects, the following is used:

AppClient.saveDeferredActions().then(function(){
 //history saved
}, function(error){
 //error
});