Skip to main content
OutSystems

OutSystems Mobile Best Practices

When developing mobile apps there are some important aspects that you should take into account:

  • Your app will be installed in all sorts of devices, including mid-range and low-end smartphones;
  • Your end-users will be on the move, facing intermittent network conditions and offline periods.

No matter the circumstances, your end-users will expect your mobile app to work like a charm! They expect to have a great experience using your app wherever they are.

This article aggregates a set of principles and best practices that you should follow when developing a mobile app using OutSystems. They will help you to avoid common pitfalls that can compromise the performance and user experience of your app.

This compilation of best practices was built based on what we have already learned from our developers. We’ll be updating and adding new contents along the way!

The Six Principles

Best Practices

Improve Application Loading 

It’s common for a mobile app to have a splash screen where the app downloads the latest version of the resources it needs to work so it can optimize the user experience (from that point on everything is on the device side). 

In OutSystems, only after those resources have been downloaded the app will navigate to the homepage to ensure the best user experience.

Since there is a limited amount of connections opened at the same time, the content of the splash screen will compete for connections with the upgrade process of the app, increasing the display time of this screen before navigating to the homepage.

Keep the splash screen simple and fast to load, so avoid adding:

  • Complex UI;
  • Heavy logic;
  • Requests to the server;
  • Fetches of external resources.

In these cases, you should evaluate a better alternative of implementation than the splash screen.

Adapt Images

Images are frequently the biggest files of a mobile application and downloading them can take a lot of time, thus impacting the performance of an app, namely for old devices and situations with connectivity issues.

To optimize your images for a mobile app:

  • Reduce image size and dimensions for the best experience in the mobile devices of your target users (check more info). Large images can consume considerable storage size, increase download time and won’t probably fit the dimensions of the mobile device screen;
  • Set the Image widget to have the image dimensions. This way you will avoid the widget from changing to adapt to the image size after it has been downloaded;
  • Delay fetching and rendering images (like advertising banners) until more important content is fetched and rendered (see how to Prioritize Screen Content Rendering). This way you will minimize the impact of loading images on the screen rendering. 

Optimize Lists

When displaying records in a list, the user experience and the performance of the app can be compromised if there is a great number of records. This is particularly relevant for mobile devices or scenarios with connectivity issues.
 
To optimize and avoid these problems there is a list of best practices you should follow:

  • Design an empty slate for a list item

When you navigate to a screen, data of the screen starts to be fetched asynchronously to the screen rendering, i.e., runs parallel to the screen render and doesn’t block the render of UI widgets. In this case, the list and its items will appear on the screen but if the data used in those items is not yet fetched, they will display the static content and right after the data is fetched the items will be updated. This will make your items to expand to afford the content when data arrives creating a weird user experience of content moving in the display.

To prevent this UI experience from happening, you can create an empty slate for the list item. Set the size of the list item to be the expected final size when the content is loaded and add some illustrative content that you will remove right after the content for the list item is available (more info in the Design a Blank Slate for Content Being Fetched section or in How to improve list slowness on low-end devices article). This strategy is also valid for blocks, cards, and other elements of the page waiting for data being fetched.

  • Avoid expanding content in the list item

Avoid list items with content that can be expanded by the user as, for example, a ‘Show Details…’ link to expand content in each list item. This will impact the behavior of the list and the user experience.

  • Avoid complex logic and widgets

Avoid adding complex logic or widgets to list items as, for example, complex JavaScript to load a map from Google Maps. This can easily affect the performance of the list.

  • Fetch only the list items to display

Optimize the fetch of the records to display in a list so the experience and the performance of your app is not affected while scrolling down the list. Have in mind some conditions for the strategy you adopt, as the device and the network conditions. Most common strategies are to fetch all records at the beginning or fetch records as they are needed while the user scrolls down.

As an example of this last strategy, imagine the situation where you navigate to a screen and the list will initially display 20 items. You can tune the query to only fetch those 20 records, instead of all records. Then, use the On Scroll Ending event of the list to fetch the next 20 records to be displayed as the user scrolls down, and so on.

Prioritize Screen Content Rendering

When rendering a screen, the app establishes connections to fetch data for Aggregates and Data Actions defined in that screen. Since there is a limit to the number of connections open at the same time, content may take longer to load while waiting for connections to be established.
 
By default, a mobile app fetches screen data without any specific priority. This means that in situations the screen has content that is more relevant to be displayed first, it is not guaranteed it will. This situation may lead to a not expected user experience.

To prevent those situations, you should prioritize the content that should be rendered first and delay the secondary content rendering as, for example, delay the rendering of an advertising banner.

To delay the rendering of content, start by placing that content inside a Block and putting that Block inside the True branch of a If widget. Set the If condition with a variable holding False by default. On the OnRender event of the screen, add logic to set the variable to True for the delayed content will start to render.

Design a Blank Slate for Content Being Fetched

When a user navigates to a screen in the app he expects to see content very fast. If the content takes time to load and he sees an empty or unfinished screen, he may think the app is stuck or working badly. This happens because the data for the screen was not fetched yet and only static content is rendered on the screen.

You can improve this experience by designing a blank slate for the widget that will be displayed while the data is being fetched. 

Optimize Fetching Data for a Screen

Fetching data for a screen can have impact if fetching too many data or using complex queries. It can make the screen rendering last a considerable time before it’s complete.

A scenario is when fetching data from the server and an aggregate depends on the result of another aggregate, i.e., they have to be executed in sequence. Avoid the On After Fetch event to trigger Aggregates in sequence. Instead, create a Data Action and execute the aggregates in the correct order. 

Check the next section for best practices regarding the local storage.

Design a Lightweight Local Storage

An OutSystems mobile app can keep data in the device to enable offline or cache scenarios. Although this can be used to significantly improve the app performance, it might easily become a performance killer if not used correctly, especially if the device is a lower end device.

When using local storage keep in mind the following:

  • Optimize the amount of data stored in the local storage. Avoid storing data you don’t need for your use case. For example, store in the local storage only the entity records that you will really need from the server, instead of all entity records;
  • Design the local storage data model for your use cases. Avoid complex queries (with too many JOINS), since they might impact the app performance. To avoid the use of multiple JOINS, you can denormalize the entities model in the local storage. Or, in case you are importing an entity from the server, recreate only the attributes that you need, instead of all attributes of that entity in the server;
  • Identify the best data synchronization patterns for your use cases and exactly when to run them (check data synchronization documentation).

Use CSS for Animations

Prefer CSS capabilities to perform animations and transitions rather than using JavaScript. Use animations with hardware acceleration and run your animations using GPU (Graphics Processing Unit) than CPU (Central Processing Unit). Check more info by reading the “Smooth as Butter: Achieving 60 FPS Animations with CSS3“ post.

JavaScript Best Practices

Prefer OutSystems Low-Code to JavaScript

OutSystems produces fast and optimized code for performant mobile apps. It provides a set of built-in capabilities to create those apps, but it also allows developers to implement their own code using JavaScript to cover specific use cases.

Before using your own JavaScript code, check if you can implement it instead with OutSystems low-code or with a component available in Forge.

Know and Use OutSystems Public JavaScript API

OutSystems provides a public JavaScript API that you can use the most common extensibility scenarios and use it in JavaScript nodes. Don’t use internal OutSystems JavaScript API’s since they are not supported and might break on a platform update.

Change Widgets Style with OutSystems Low-Code

Changing widgets style by adding or removing CSS classes using JavaScript is a possible source of issues. For example, avoid doing the following:

var input = document.getElementById($parameters.inputId);
var displayLoader = $parameters.displayLoader;
var loaderClass = "input-loader"

if (displayLoader) {
    input.classList.add(loaderClass);
} else {
    input.classList.remove(loaderClass);   
}

To change the CSS classes of your widgets, use the Style Classes property of that widget. This property can include expressions that are evaluated at runtime, thus allowing to dynamically change the styles of the widget. For example:

Avoid Manipulating the DOM

Mobile apps in Outsystems have the runtime strategy optimized for fast/optimal rendering. As such, apps may not behave well with arbitrary and unpredictable DOM manipulations, which can lead to errors and bad performance issues (and most of them may not be visible). 

However, if you really have to manipulate the DOM directly:

  • Make sure you first understand how the lifecycle of the app and components work;
  • Use the On Render event to execute your code after OutSystems elements are rendered;
  • Use the On Destroy event to execute your code after OutSystems elements are removed when they are destroyed.

Avoid Creating Global Variables

OutSystems uses a SPA (Single-Page Application) approach to deliver mobile with great performance. This mechanism optimizes the runtime execution of mobile apps and one of its particularities is that the Window object isn’t cleared between navigations. As a consequence, global objects aren’t destroyed. 

Avoid attaching objects to the window object like cache objects, variables, and listeners. Instead, you should do the following:

  • Use browser’s supported features as localStorage or sessionStorage objects;
  • Declare a variable using the ‘var’ keyword to create it in the scope of the function, or it will be attached to the window object:

myVar = "hello"
var myVar="hello"

Avoid Using External JavaScript Libraries

Avoid importing and using external JavaScript libraries that are computationally complex in your mobile app (e.g.: jQuery). Most of them were not created to run on mobile devices and their use can have impact on the performance of your app. You should also avoid libraries that manipulate the DOM since they will do concurrently with the  OutSystems runtime.

Keep in mind that using an external library will never be as fast as an OutSystems component, therefore, do it only if there is no built-in component available. In case you really need to import a library to use in your app, see how to Use JavaScript Code from an External Library.

Use Performant JavaScript Selectors

To obtain elements from the DOM use specific selector queries (like .getElementById) rather than global search selectors (like .querySelector). In case you have to execute the same selector search many times, cache its value in a variable. 

For more info check this comparative test between selectors or read this link.

Slow JavaScript Functions

The JavaScript API supported by the browsers provide a set of properties and methods that will trigger the browser to calculate the geometric information for the elements. This calculation process is called Layout or Reflow, and can have significant performance impact. Some examples are the .offsetHeight property and .getComputedStyle. Check here the list of properties and methods that will force Layout/Reflow.

If you have no alternative to use them, cache their values to prevent re-executing them.

Move JavaScript Libraries to Scripts

Move libraries from JavaScript nodes to Script elements. This way you will develop:

  • A cleaner code;
  • A single definition used by all code;
  • Avoid multiple parses of the same code thus improving performance.

See Use JavaScript Code from an External Library for more info.

Refactor JavaScript Code Into Client Actions

If you are calling the same JavaScript logic multiple times, move that code into a client action. You can then call that same logic when needed through other client actions (screen or global) or even inside a JS node. This way, you will isolate your JavaScript code, avoid a repetitive definition of the same logic, and improve your app maintainability.

Follow the Industry JavaScript Best Practices

When writing JavaScript make sure you know the code you’re implementing and how it affects your app besides the feature you’re implementing. Follow the best practices for JavaScript development as you would do in Web development. 

Check the W3C JavaScript Best Practices or this article.

  • Was this article helpful?