Detecting if a user is offline

How do I check whether a user is online or offline in my app's JS?

I want to disable a certain workflow if the user is offline.

Online/offline is not binary. There are many other intermediate states such as a slow network or intermittent connectivity, which cannot be detected by a single check. The best is to design the online process to degrade gracefully if it fails with a network error.

That being said, you can do a check to see if you reach the JourneyApps servers. You can create a CloudCode task that you call from the app to check for connectivity.

is_online: index.js

Please activate the Apptrigger on your ClodeCode task.

export async function run() {
    return 'Hello from the other side';
}

App side JS code:

Then from your app side JS code, wrap the CloudCode call in a try-catch. If it succeeds, you know that you are not offline.

function isOnline () {
    try {
        var result = CloudCode.callTask('is_online');
        console.log(result);
        return true;
    }catch (e) {
        console.log('Not online! ', e);
        return false;
    }
}
2 Likes

@DavidHoltzhausen what does ā€œThe best is to design the online process to degrade gracefully if it fails with a network error.ā€ mean? Could you elaborate?

@KobieBotha Currently OnlineDB calls fail with an ugly message HTTPS 0 Error. You could wrap this in a try-catch and either retry or display a more descriptive error: function getOnlineData () { try { // OnlineDB call } catch (e) { console.log(Online error ${e}); // Do something else dialog(ā€˜Offlineā€™,ā€˜Unable to connect to the online server, please try again.ā€™); }}

This is good advise, except that when you catch an error in a helper function, then the parent function does not know something went wrong and it continues to run. You could use throw ā€˜Internet errorā€™ but this still produces a dialog with the title ā€˜Errorā€™ which is also not ideal. Can you suppress the throw dialog?

@martin the function in the example returns either true or false based on if it is online or not. So you could take out the console.log in the catch and leave it up to the parent function to decide what it needs to do with the feedback. The function was just a suggestion for something to start with :slight_smile:

The current solution is simple and elegant, but has one big drawback and that is that it doesnā€™t take poor connections into account.

Basically, as long as the initial online check succeeds, regardless of how long it takes to succeed, it is going to return ā€œtrueā€ | ā€œonlineā€. Now this is technically correct (as explained previously, if you can connect you are technically connected|online), but not necessarily practical. This is especially true if all subsequent calls, which will most likely be more resource intensive than the original check, will also take a really long time to resolve.

Therefore, it may be a good idea to time the online check and have a threshold / benchmark duration that you can compare against, and only return ā€œtrueā€ | ā€œonlineā€ if the online check succeeds and succeeds in a timely manner.

Something like this:

function checkConnection() {
 var threshold = 2000; // threshold in milliseconds

 // The threshold is basically an indication of the max time you expect the call to OnlineDB.user.first() to take
 // This will vary based on connection speed and strength
 // Here are some basic examples, but your mileage can and will vary
 // RESULTS: 
 // WiFi: ~ 130ms
 // Strong 3G: ~ 550ms
 // Slow 3G: ~ 2000ms

 var before = new Date();
 // The app will have to wait for this to execute and so will spin and wait until it completes or times out by itself
 var attempt = attemptOnlineCall();
 var duration = (new Date().getTime()) - before.getTime();

 if (attempt && duration < threshold) {
   // Connected, and connected fast enough
   return true;
 } else {
   // Not connected, or connected too slowly
   return false;
 }
}

function attemptOnlineCall() {
 try {
   var user = OnlineDB.user.first();
   return true
 } catch (err) {
   return false;
 }
}
3 Likes