Taking your Worklight apps offline

What happens when your users are disconnected? Or when they enter an area with poor connectivity or are out of your company’s secure network?

Well, don’t panic, we have got you covered!

In this article I will give some guidelines and methods that will get you started with taking your Worklight applications offline.

The sales pitch

Almost all sales pitches include several offline scenarios such as:

  • The user can still take notes and pictures while on site and upload them when they get back to the office.
  • The user can download some brochures or files to present to his clients at the clients location.

There is no reason why your app can not be useful when the user goes offline!

You can still allow them to capture data, do some analysis, make decisions and there are a lot of useful phone features that you can use that are not dependant on network connection such as: location (via gps), compass, camera, sound recording, file system, local storage.

The reality

It’s hard to find good examples of implementing these offline scenarios. The two difficult aspects that always come up are:

  1. Authentication
  2. Synchronisation

Offline Authentication

You can find a good example app that implements the concepts below in this post.

This may sound a bit strange, but in order to authenticate offline, the user must first authenticate online! You can’t really avoid doing at least one validation that your user exists on your servers.

But once this is done, all we need to do is save the users’ authenticated credentials encrypted on the phones’ storage and then next time we authenticate against that.

The logic can be something like this:

Get authenticated users object from server -> encrypt it -> save to localstorage

var tempUser = {username:”PapaSimons”, password:”ilovewl”};
var encyptedUser = md5(angular.toJson(tempUser));
localStorage.setItem(offlineDB, encyptedUser);

Then next time we login and we are offline:

Get input from the login form -> encrypt it -> compare to saved data

var formAuth = md5(angular.toJson(loginUser));
var savedAuth = localStorage.getItem(offlineDB);
offlineAuthed = (savedAuth == formAuth);
if (!offlineAuthed){ authError = “* Wrong login details.”; }
if (savedAuth == null){ authError = “* You have to go online first.”; }

We are using a js md5 library to encrypt the saved data. Md5 is a one way encryption algorithm, which means that you don’t decrypt it. Instead, you just encrypt the same input again and compare the generated keys.

This means that if someone manages to get a hold of your apps’ localstorage data, they would have a difficult time working it out. To make things a bit more secure, I am encrypting the whole stringified JSON object. You should find your own clever ways to make the encryption secure, the most common way is to add a SALT.

Localstorage is the built in html5 storage standard, you can use it pretty much out of the box here. It’s only down point is that it may not persist as well as other storage mechanisms like JsonStorage. The data may get lost for example when the user re-installs the app. I am OK if the localstorage gets lost as I do want to encourage server authentication in those extreme cases.

Bonus options:

  • Add an expiration date to the user object and check todays date > expiration date. This way you can enforce the users to authenticate with the servers if their passwords expire every x months.
  • Multiple users login can also be handled by using the username as the storage key.
localStorage.setItem(username, encyptedUser); //save
localStorage.getItem(username); //retrieve

Synchronisation app-server

Data synchronisation is something that can get tricky in many situations.

In some cases your users can add new items, edit or delete them.. In other cases, your users may be manipulating their items through different devices or even co-editing them with other users. Each device or editor may potentially go offline and online.

The quick and non-scalable method:

The lazy option, is the easiest to implement but is also the least scalable. The idea is to just dump the whole data set back and forward between the app and server. This can work when you datasets are small and you can assume that the users are just using one device at a time.

The timestamp method:

Another option is to use time stamps to check when the last sync has been done. This goes both ways, the server should know what items that he is receiving are the latest and the app should know what items to ask for.

The advantage here is that only the latest things are transferred and therefore saving a bit of bandwidth.

The disadvantage is that things start to get complex to maintain and you have to start implementing and patching up your logic down the road to many scenarios.

The dirty method:

This is another option that Worklight has built into the JSONStore api. The logic is to mark whatever data that has been changed within the app as dirty and whatever data that has been synchronised with the server or came from the server as clean.

function (dirtyItems) {
//push dirtydocs to the server
}).then(function (responseFromAdapter) {
}).then(function (){ //success });

This way you just need to transfer the affected items on the server and deal with them on the server side.

After that you can clean up your app dataset and fetch the latest data from the server.

Make sure that when you add, remove, or modify your data you mark them dirty.

WL.JSONStore.get(COLLECTION_NAME).add(item,{markDirty: true}).then(function(){ //success });
WL.JSONStore.get(COLLECTION_NAME).remove({_id:id},{markDirty: true}).then(function(){ //success });

JSONStore will not remove dirty data until you mark it clean again, this way you can notify the server to remove the removed items in the next sync.

User experience considerations

The following guidelines are important and will save you a lot of money and headache on customer support (if you company actually does these kind of things ☺).

  • If how updated the information is, is crucial, as it is for example in stock data then it’s important to show “last updated on — xx/xx/xxxx hh:mm”.
  • Give proper and meaningful error responses, that are different when the users is online or offline.
  • Highlight what are the things that are not available offline by disabling/greying them out.. There is no need for the user to click on something that doesn’t work only to find out that it doesn’t work.
  • Don’t allow time critical transactions, such as booking a taxi etc.. as they may be never launched or not relevant when they get synced.
  • Don’t give your users the illusion that their offline actions have been accepted or in process.. instead let them know that they have to go online first.