|The capabilities described on this page are included in Appian's advanced and premium capability tiers. Usage limits may apply.
Offline forms have different needs than online forms. Because the forms need to work while the user is offline, they need to avoid functionality that requires a connection to the server.
Furthermore, the data that will be used to load the form will only be collected when the user is online and their actions or tasks are refreshed. When this happens, we load the form in the background to query the data that the user will need when they are offline.
Keep in mind that offline-enabled forms accessed on mobile always act as if they aren't connected to the server, even when online. Fully test offline forms on a mobile device.
This document outlines the design considerations to be aware of when designing offline forms.
To use the newest offline mobile capabilities, make sure that your mobile app is on the same version as or newer than your server version.
To help make sure your forms will work offline, make sure that they adhere to the following design guidelines:
IMPORTANT: When testing offline actions and tasks, test the entire action or task on mobile and complete all fields. Otherwise, users may run into issues at runtime that will cause them to lose their work.
All data and external information must be loaded into local variables or rule inputs at the top of the interface expression, so that they evaluate on the first load of the interface.
This is important because this is the only information that will be available to the user when they are filling out the form. Offline-enabled forms must retrieve any external data when the form is first loaded and downloaded to the device. Attempting to query for data when the user is offline will cause an error since they can no longer connect to the data source.
Instead of querying for your data inside of the component where it will be displayed, define a local variable at the top of the interface expression. Use this local variable to query and store that data to be used throughout the interface expression.
For example, in a typical interface, you may want to query data after a certain section is shown based on user input. However, in an offline interface, the form would break because it is trying to query the data after the initial load. But because the user is offline, it can't get the data.
In this example,
rule!getAllCustomers() could be any number of methods used to get data such as
a!queryEntity() or an integration.
When you have a parent interface that is made up of multiple child interfaces or rules, make sure that you are getting all of your data in the parent interface. Then pass this data to the child interface or rule using rule inputs.
When the application loads the action or task to cache the data for offline use, only the data in the parent form is loaded. If you try to load the data in the child interface or rule, it will cause an error.
You can still use local variables in a child interface, you just need to make sure they only use functions that are supported for offline reevaluations.
In the following example, we query the data into a local variable at the top of the parent interface expression. We then use that variable for the data parameter value for the child interface.
In the child interface, we use a rule input for the data parameter. This ensures that no querying happens in the child interface.
Child interface: `rule!childGridInterface()`
Child interface: rule!childGridInterface()
When building an offline form, use CDTs to access and work with your data. There are two ways to query your data for offline forms using CDTs; you can use
If your data is easily accessible in a data store entity, you can use
a!queryEntity() in a local variable at the top of the form. While you can use the dictionary returned by the query, we recommend that you cast the results to a CDT so that the data is easier to work with.
To cast a dictionary to a CDT in an offline form:
a!queryEntity() in a local variable at the top of your form to query your data.
For an example of casting the dictionary to a CDT, see the Only query the data you need section. That example highlights both casting and limiting your query results to improve performance.
If your data is stored in a record type, you can access it using
a!queryRecordType() as long as you cast it to a CDT. You cannot use
a!queryRecordType() in an offline form without casting it to a CDT.
To use record data in an offline form:
a!queryRecordType() to query your data within the cast.
For more information about record types in Appian, see the Appian Records documentation.
Only load the data that you need in the interface. For example, instead of querying all of the records in a record type, only query the record that you need and the specific record fields that you are going to use.
This is important to improve the performance of downloading and refreshing offline-enabled actions and tasks before the user goes offline. If you are querying too much data, it may slow down refresh times for users.
Instead of querying an entire record, filter the query so it only returns the fields and records that you need.
Before you create your interface, map out the data that you think you are going to need.
For example, if you are designing a home inspection form, you may need the following type of information:
At the top of your interface expression, define your local variables so that they only query the information to be displayed. For example, if you only needed the customer's name, address, and phone number, you wouldn't query the entire customer record. Instead, you would only query the name, address, and phone number fields.
You can use charts and read-only grids in offline forms, simply use a query and a data store entity to access the data in your CDT. At the top of your interface expression, use
a!queryEntity() and cast the query result to a CDT inside of local variables or use rule inputs. Make sure to follow our querying data guidelines and only query the relevant data.
For more information on how to configure a read-only grid using a query, check out our read-only grid tutorial.
For more information on how to use charts without record types as a source, check out our chart examples:
Aside from using
a!queryRecordType() and casting the results to a CDT at the top of the page, offline forms can't access record types. This means that any function, parameter, or functionality that relies on access to record types won't work. The lists below specify the parameters for charts and read-only grids that you should avoid.
The following read-only grid parameters won't work offline:
The a!recordData() function, any record reference in data parameter, refresh functionality, and record actions are also not available for use in offline read-only grids.
The following chart parameters won't work offline:
The a!recordData() function, any record reference in data parameter, refresh functionality, and record actions are also not available for use in offline charts.
We have already talked about only loading the data you need. Likewise, if you are allowing the user to update information, you should only write the fields that are being updated.
When you write a CDT to a database, all of the fields in the database get updated, whether you entered a value or not. This means that if you're trying to only update the first name in a database using a Customer CDT, if you don't set the values for all of the other fields, you could overwrite all of the other fields with null.
To prevent this, create a CDT that only includes the fields that you are updating. Use this CDT to write only the updated fields to the data store.
Keep in mind that when we need to deal with conflicting database entries in Offline Mobile, we choose the most recent database entry as the source of truth.
For example, you have an offline interface that queries for the following fields at the top of the form.
In the form, you allow the user to update the customer's address, but no other fields. To write this information to the data store, you need to create a CDT with only the id and the address fields. If you were to write to the data store using the original CDT, when you write the new address, you would update all of the other fields to
Plug-ins are not supported in offline interfaces. This includes function, smart service, and component plug-ins.
While many functions and components work offline, there are some that won't due to their nature. Certain smart service functions and interface components only work if they are connected to the server. Therefore, they cannot be used for offline forms.
For more information on components and functions that are incompatible with offline forms, see the Appian functions table. To search for functions and components that aren't compatible with Offline Mobile, be sure to change the compatibility to Incompatible or Partially Compatible with Offline Mobile.
Functions Partially Compatible with Offline Mobile need a live connection to the server in order to work correctly. However, when the user is offline, they won't have access to the information from the server in order to work properly. In the Appian functions table, functions and components that aren't supported for offline reevaluation are listed as Partially Compatible.
If you need to use one of these functions, keep the following best practices in mind.
Functions such as
supervisor() can be used in offline forms. However, these functions require a connection to the server in order to get their value. In order to use these in an offline form, load the data at the top of the interface expression into a local variable.
Only data that is queried on the initial load of the form can be used in offline forms. If you were to try to use these functions later on in an interface expression, it would cause an error since there is no connection to the server.
For example, even though
loggedInUser() isn't supported for offline reevaluations, you can still use this function by saving the resulting value into a local variable at the top of the interface expression.
When the user is online and refreshes their action or task list, the form will automatically load in the background and get the value for
loggedInUser(). When the user goes offline, it will use the value that was updated when they last refreshed.
If you tried to use
loggedInUser() in the saveInto parameter, it would return an error when the user is offline since it cannot connect to the server to get the value.
For functions that aren't supported for offline evaluations, when you use them in a local variable you should use
a!refreshVariable() to set the value of refreshOnReferencedVarChange to false.
By default, all local variables created with
a!localVariable() will automatically refresh whenever a variable they reference is updated. If that refresh were to happen while the user was offline, it would result in an error. In order to prevent this from happening, use
a!refreshVariable() and set the value of refreshOnReferencedVarChange to false.
For example, imagine you have a list of users that you want to query. However, the username is in the format "firstname.lastname" and you would like to display it as "Firstname Lastname."
If you are designing the form for offline use, you would want to store the list of users in a local variable at the top of the form.
You can then store the prettified version of the names into another local variable. If you use
a!refreshVariable() and set the value of refreshOnReferencedVarChange to
false, you can ensure this variable is only evaluated when the interface is first loaded. If refreshOnReferencedVarChange is not set to
local!users is updated in a saveInto somewhere else in the interface,
local!usersForDisplay would automatically update and attempt to use the
user() function offline, which would result in an error.
You can now download documents for offline use in forms. Whether you want to include an image for a stylized billboard or bring in reference documents specific to user tasks, you're able to include any document in offline forms for your users to download.
To download documents offline, simply save your document as a document data type or as a document or folder data type and use a document download link to call the document using one of the three following methods:
For all three methods, you'll need to do all your calling, querying, and casting at the top of the form. For more information on querying and casting query results to a CDT, see our sections on pre-loading data and querying and writing data.
In some of your offline forms, you may want to include a document that won't regularly change. These could be an image to show in a billboard, a document with additional details and instructions, or any document that won't change from user to user.
There are two methods that you can use to download these kinds of documents; a constant or the
When building offline forms, using a constant is the easiest and recommended method for calling in a document that won't regularly change. Simply create a constant that calls your document and use it in a local variable at the top of your form.
If the document that you want to include in your offline form is saved as a document ID of type integer, you need to cast it to a document type.
To cast the integer to a document within the interface, use the
todocument() function within a local variable at the top of the form. If you use it farther down in the expression, the interface will attempt to return the document while offline and will not be able to retrieve it.
In some of your offline forms, you may need to include documents that will change based on the user's needs. These could be reference images or guides specific to an inspection or previously submitted photos for equipment comparison.
These documents are typically stored in database and you can access them using
a!queryEntity(). For more information and examples of using query entities, see our guidance on querying data store entities and casting the results.
Make sure that the document you want to include is stored as a document data type or as a document or folder data type. If your document is saved as any other data type, you must cast it to a document data type or a document or folder data type in a CDT so that you can download it. You can do this within local variables at the top of your form.
The following is an example of saving data as a document data type in a CDT.
The following example queries a data store entity and casts the document ID that it receives to a document data type using the Inspection Item CDT shown above.
/*Expression to display data*/
Some changes and configurations may prevent pending offline forms from being submitted. Below are some best practices so that you can avoid changes and configurations that may cause pending forms submission failures.
If your offline form is using a type constructor to reference or save values to a CDT, be careful about making changes to the CDT.
Certain changes to the CDT, such as adding a new field, are fine. However, deleting a field or changing a field's name or type aren't backwards compatible with older versions of your CDT. This means that if you make a change to a CDT that is used by an interface with a type constructor and that interface has any pending forms, the change will prevent the form from being submitted and the user may need to fill out the form again.
To avoid potential submission failures:
If you are using a custom process calendar and it changes while users are offline, the change will prevent any pending forms from being submitted and the user may need to fill out the form again.
To avoid potential submission failures, only make changes to your process calendar at times when there are no users offline.
If offline users attempt to upload a file in an offline form to a folder that they don't have permission to access, it will prevent the pending form from being submitted. Because the offline form cannot check that the user has permission to access the folder until the user is back online, the user won't see an error when they complete the form.
To avoid potential submission failures, make sure that any user of the offline form has permission to upload documents to the target folder.
Remember me authentication enables users to sign in once and stay signed in for a specified number of days. It can be configured in the Admin Console for Appian authentication and SAML authentication.
If a user is offline for longer than the session timeout period and remember me is not enabled, pending forms will not be submitted until after the user signs in again. To allow pending forms to be submitted automatically when the user is back online, enable remember me for the users that require this capability.
Design Best Practices for Offline Mobile