Skip to content

Setting Up an Application

Set up a server-side application and initialize the offline store.

Setting Up Server-Side Application

You must set up a server-side application on SAP Cloud Platform before an offline application that uses the framework on a client device can access the SAP Cloud Platform and the back end.

Note

A supported version of an OData service must be available and accessible by SAP Cloud Platform (such as Netweaver Gateway, Integration Gateway, SAP HANA, or a third-party service such as Apache Olingo). See Version Support and Limitations for details on Version 2.0 and Version 4.0 support.

  1. Determine your data requirements:
    1. Identify the portions of your application that are to operate offline, and the OData entity sets to be referenced.
    2. Compile the list of entity sets into an initial list of defining queries. Defining queries identify the entities and relationships that are required to operate offline.
    3. Reduce the overall data transmitted to the client. Often an application requires only a subset of the data exposed through the OData service. Review each defining query to determine if you can reduce the amount of data by using the $select or $filter OData query options, or other options. Add those OData URI primitives to the defining query.
    4. Identify shareable defining queries. For security or performance reasons an OData service may filter the results that are visible to the user that is logged in. In other cases, the result set may be the same for all users of the application. Such defining queries can be marked as shared on SAP Cloud Platform, which optimizes back-end requests. Review your defining queries and identify any that meet this "shared" definition.
  2. Create the application configuration (.ini) file (only if non-default behavior is needed) for the server-side application.

    An application configuration file consists of one or more endpoints, each of which can be optionally followed by one or more defining queries:

    1. Endpoint – identifies and determines how OData services from which the offline application retrieves data and stores offline data is processed by SAP Cloud Platform: how the offline store is prepopulated before it is downloaded to the client, indexed, and so on. See Application Configuration File for further details.
    2. Defining Query – defines various characteristics of how the retrieved data for a given endpoint is managed by SAP Cloud Platform: whether or not the data is shared, refresh interval, delta tracking, and so on. See Defining Queries for further details.
    3. On SAP Cloud Platform:
    4. Create the application. See: Defining Applications.
    5. Configure the connectivity. See: Defining Connectivity.
    6. Configure offline-specific settings. See: Defining Offline Settings for Applications.
    7. Import offline settings from the application configuration file. All settings have default values, so you'll need to create an application configuration file only if you require non-default behaviors. See: Defining Offline Settings for Applications.

Both application code and configurations on the server side work together to meet your needs.

Working with an Offline Store in Client Application

See for details.

Working with an offline store in a client application involves the following steps.

  1. Initialize and open an offline store. An offline application can manage multiple stores if required.

    When an offline store is opened for the first time, it will be populated by performing an initial download (which is built into the open operation). For this step, the application must have network connectivity. The application communicates with SAP Cloud Platform, which collects data from an OData service based on the defining queries, creates the database that stores the data, and pushes that database down to the offline store on the client device.

  2. Perform any necessary operations (create, read, update, and delete) on the data in the offline store.

  3. Send pending modification requests to the mobile service (using the upload operation). For this step, the application must have network connectivity.
  4. Download data from the back end based on defining queries and merge it into the offline store. The application must have network connectivity. See Synchronizing Data for more information.
  5. (Optional) Test the offline store. See ILOData.

Initialization of the Offline Store

An offline store is created for a given service URL and a set of defining queries. The service URL refers to an endpoint in the server-side application that you set up earlier. There should only be one offline store per endpoint. You can have multiple offline stores per application.

The offline store is represented by an OfflineODataProvider class instance constructed using the service URL and a set of defining queries. The provider instance is passed to a service object that is either a DataService class instance, or in the case of proxy classes, an instance of a generated subclass extending DataService. It is used by the offline application to interact with the local OData service and is constructed using an OfflineODataProvider instance which it delegates to for executing requests against offline store.

Note

When you see ... in the code samples in the guide, you need to replace it with your own code to make it work.

First, you need to have an SAPURLSession object in place. During onboarding, you will have access to a valid object via OnboardingContext.sapURLSession. You may also construct a valid object in your own approach.

Second, you need to implement the OfflineODataDelegate in order to get progress updates, offline store state changes, etc. Below is a sample implementation:

class OfflineODataDelegateSample: OfflineODataDelegate {

    public func offlineODataProvider(_: OfflineODataProvider, didUpdateDownloadProgress progress: OfflineODataProgress) {
     /// Handle the progress
        ...
    }

    ...
}

Then, initialize OfflineODataProvider as follows (you need to have the offline store name, mobile service application ID, and service root in place):

/// Set up parameters
var params = OfflineODataParameters()

/// Assign a store name
params.storeName = ...

/// If you know that the back-end OData service or mobile services support repeatable requests, enable repeatable requests on client
/// so Offline OData will generate a repeatable request header for each request.
/// If none of them support repeatable requests, you should disable this on the client.
/// Be aware that disabling repeatable requests can lead to multiple executions of the same requests in some cases
params.enableRepeatableRequests = true

/// Use server-driven paging with a page size of 100.
/// Note that the "server" here refers to the offline store as a local service, not the back-end OData service
params.pageSize = 100

/// The SAPURLSession object can be fetched from the onboarding context.
/// During onboarding, the SAPURLSession object should have
/// successfully registered to mobile services with an application ID
let sapURLSession = ...

/// Create delegate object
let delegate = OfflineODataDelegateSample()

/// Create OfflineODataProvider object
let offlineODataProvider = OfflineODataProvider(serviceRoot: ..., parameters: params, sapURLSession: sapURLSession, delegate: delegate)

/// Add defining queries
try offlineODataProvider.add(definingQuery: OfflineODataDefiningQuery(name: "req1", query: "/Events", automaticallyRetrievesStreams: false))
...

/// Open the provider with a completion handler. The open() function is asynchronous
offlineODataProvider.open(completionHandler: {(_ error: OfflineODataError?) -> Void in
    if let error = error {
        /// Handle the error
        ...
    } else {
        /// Proceed to use the offline store after it is successfully opened
        ...
    }
})

The provider needs to be closed if it is no longer needed:

/// Close the provider
try offlineODataProvider.close()

Offline-specific operations, for example upload and download, are not available from the DataService class. They can be accessed using OfflineODataProvider. Due to potential long execution time, upload, download and open calls can only be invoked asynchronously. Should the operation fail, the error parameter for the completion handler will contain detailed information.

Data Encryption with an Offline Store

Before deploying your app to production, you must enable offline store encryption to protect sensitive business or personal data (although you can omit data encryption for development and testing purposes).

By supplying an encryption key, the offline store is able to provide strong encryption, using the AES 256-bit algorithm, to provide security against skilled and determined attempts to gain access to the data. Encryption does come with a small impact on performance. The scope of the impact depends, in part, on the size of the cache.

When the offline store is opened for the first time, an encryption key must be supplied to enable encryption using the OfflineODataParameters class. Once the offline store is encrypted, the encryption key cannot be recovered from the offline store itself and must be presented on subsequent opens. It is critical to securely store the key.

The encryption key for the offline store can be stored securely in the SecureKeyValueStore. Each time the offline store is opened, the SecureKeyValueStore must also be opened. You should enforce a mobile passcode to protect a mobile app from unauthorized access. The mobile passcode derives an encryption key that secures a SecureKeyValueStore. The passcode is typically required during onboarding, subsequent application launches, and returning to foreground. Neither the passcode nor the derived encryption key for the SecureKeyValueStore is ever stored.

The offline store is encrypted using an AES 256-bit cipher. The encryption key is used as an input to a password-based key derivation function. The length and complexity of the encryption key is critical to ensure that brute force guessing attacks will not succeed.

Consider the following when creating a key:

  • Do not include semicolons in your key.
  • Do not put the key itself in quotes, or the quotes are considered part of the key.
  • Lost or forgotten keys result in completely inaccessible stores.
var params = OfflineODataParameters()
/// Set a strong random key of at least 16 characters
params.storeEncryptionKey = ...
/// Proceed to open OfflineODataProvider with the parameters
...

Multiuser Considerations

If the application is to support multiple users on the same device, you can specify a unique store name and store path for the offline store for the given user.

To avoid offline store files clashing among different users, you can use OfflineODataParameters to instruct OfflineODataProvider to store the database files under separate directories with user-specific names by leveraging information from SAPcpmsUserInfo.

/// Set user id
let userId = ...

/// Create directory for offline store files if needed
let fm = FileManager.default
let storeFilePath = getDocumentsDirectory() + "/" + userId
if !fm.fileExists(atPath: storeFilePath) {
    try fm.createDirectory(atPath: storeFilePath, withIntermediateDirectories: false)
}

var params = OfflineODataParameters()

/// Set user specific store name and path
params.storeName = "offlinestore_" + userId
params.storePath = URL(fileURLWithPath: getDocumentsDirectory() + "/" + userId)
params.storeEncryptionKey = ...

/// Create OfflineODataProvider object with given parameters
let offlineODataProvider = ...

Another common approach is to remove the offline store before opening a new one for a different user.

Regardless of the approach, users should be mindful about uploading pending modification requests before releasing that device to a different user.


Last update: October 30, 2020