Non-technical Considerations when writing Offline Apps
Asynchronous vs Synchronous
What does this mean? An asynchronous call is one where the processing happens in a separate process in the background, allowing the user to continue with other work while it happens. A synchronous process would happen in real-time and the user would have to wait until it was completed before continuing to work.
In Neptune Software, the Ajax and Rest API calls to the back-end are asynchronous by default, so to make them appear synchronous, we use App.setBusy() or Busy Dialogs (which I prefer).
You’ll sometimes hear users complaining about only seeing the “dots of death” and this almost always turns out to be the result of poor error handling. Either no closing of the Busy Dialogs in the Ajax error event or some other code that errors before the Busy Dialog can be closed.
You’ve got to think, “do I really need to block the user while the data is hitting the back-end?” There are only a few cases where I feel this is valid:
-
When doing a first-time initialization of offline data, and even this is not a must.
-
When the user requires data, such as a document number, back from the server after processing a transaction, to carry out the next step. But here we are no longer talking about offline scenarios.
-
To prevent the user from clicking the same button twice before the necessary processing takes place, possibly resulting in duplication.
Partial Offline Storage vs Full Offline Storage
What does this mean? To reduce network traffic and data volumes on your device, you may be able to store a portion of your app data offline. For example, a list of document headers only, and only retrieve all the line item data for each document from the backend when a user taps on the document in the list. This would be partial storage, whereas downloading all the line item data for all the headers at the time of synching would be called full storage.
This discussion may seem less relevant as network speed and device storage keeps growing exponentially, but as the capacity grows, so too does the demand we put on it. These days you may even consider caching images for thousands of products offline, something that would have seemed a tall order a few years ago. So it is definitely still relevant.
My feeling is, if the data you want to pull for any one entity is so large that keeping a permanent cache of it offline is out of the question, then your app does not lend itself well to offline usage. However, I would always ask the question. By using sap.n.database, or should I say neptune.offline.database, you can access a LOT of data very quickly and efficiently while offline. (Currently only available in P8). I have been involved with writing apps for customers where the offline data volumes are huge, and the only complaints about performance have always been related to the online processing portions. That is why I will always be pro full offline storage.
Full Data Synch vs Time-stamped Delta Synch
What does this mean? If you were to pull all the data for each entity set on the backend during data synching, you would be performing a full synch. On the other hand, if you were to pull only the data that has been created or changed since the last time you synched, you would be performing a time-stamped delta synch.
This one is a no-brainer for me. Both simple Ajax/RestAPI calls and the neptune.offline.database framework offer the ability to easily pass and receive a timestamp when synching, and in the neptune.offline.database it’s completely free, you don’t even have to code anything. (Except in your back-end code where you choose the data of course!)
If you are sticking to using good old JSON arrays that you have cached for your offline capabilities, and your number of records starts running into the hundreds of thousands, and your delta payloads start running into the hundreds of records, you will suffer performance issues doing the delta updates. This scenario is the only time I would recommend going to a full download. Even then, I would only recommend this as a temporary workaround until you migrate your apps to use neptune.offline.database. You will need to learn a bit of SQL code to do this, but that is always just a Google search away. If you’re only going to be reading that data, you can also just use ModelData.FindDB.
Offline First vs Online First
What does this mean? In an online first approach, we would post to the backend first, and only if that were to fail, cache the data in an offline table (an outbox), and allow for it to be posted later. There are a few variations on that approach that we could go into, but that would be too much detail for this blog. In an offline first approach, we would always assume that we are offline, and immediately cache the data for later processing. In my opinion, if you’re going to do offline, do it completely or don’t do it at all. It will take less effort this way.
When I started out doing offline apps, I would always take the online first approach. It is so easy, right? And if you want to transform a previously online only app then it is super quick to do this.
However, there are a couple of disadvantages to keep in mind:
-
Doing a busy dialog while a call to the backend happens and gets processed gives the impression of a slow app.
-
Chances are you will need to write two sets of code and two different Ajaxes/RestAPIs. One for doing the original post, and one for reprocessing the outbox.
If you write the data straight into the outbox as the user hits save, you provide the user with a sense of immediate responsiveness and you can then trigger a background process to send data to the backend at regular intervals. The user doesn’t need to even know about this unless there are errors on the backend that you want to make them aware of.
Conflicts when updating
What does this mean? It can happen if two different users access and make changes to the same backend entity while offline and then later hit the backend again. Since we are largely using SAP standard functionality to do the updating, SAP itself will often catch this for us. We can also write a custom code to check for date-time last updated vs. date-time last read the record.
How to decide what to do when this happens is strictly a business discussion. There are only a few options to choose from, so I have laid them out below:
-
Over-write the changes made by the earlier user
-
Discard the user’s changes
-
Allow the user to decide on which of the above two to carry out. (Keep in mind the level of the user)
-
Log and workflow to a supervisor to resolve
Conclusion
We would like to emphasize that each company should discuss the above considerations and arrive at an agreed approach for each project.
In summary, we feel that for a truly offline-capable app, one should aim at asynchronous data synchs, with full offline storage, time-stamped delta synchs of master data, and an offline first approach.
Having said that, bear in mind that even our offline-capable SAP apps are essentially still just front-ends. You do NOT want to (and nor should you) rewrite the whole of SAP inside your apps, so there will always be some practical limitations (not technological ones) as to how far you can go with processing while in offline mode. It is a good idea to document what those limitations are for each app so that there is a clear understanding for the user community.
References
DATA SYNCHRONIZATION PATTERNS IN MOBILE APPLICATION DESIGN ZACH MCCORMICK, Vanderbilt University DOUGLAS C. SCHMIDT, Vanderbilt University https://www.dre.vanderbilt.edu/~schmidt/PDF/PatternPaperv11.pdf