Developer Overview
Engine Auth Tokens
All of the following calls use auth tokens for Engine’s API. You can use the /appManagement/token endpoint to create these tokens.
This allows for the credentials for Engine to be safely stored and not sent to the device or other middleware implementations. This is preferred over using permanent Basic Auth credentials for most API calls from the device.
Getting SCORM Player Files From Engine
Since we have broken the player files out of the course packages, you must use the /player/zip resource to get the current player files on the device. Here is an example of this call:
curl --location --request GET 'http://[your-engine-instance]/api/v2/player/zip?player=modern' \
--header 'engineTenantName: default' \
--header 'Authorization: Bearer [auth token]’
Note: If you are self-hosted, you will need to configure the FilePathToEngineRoot setting in your RusticiEngineSettings file to specify the file path to the Engine application folder (e.g., /var/lib/tomcat9/webapps/RusticiEngine). This is required so that we can zip it up for the API response mentioned above.
Generally, you should update the version of the player files used in your mobile app whenever you update the Engine server it is connecting to, especially if crossing a major version boundary (eg, 23.x to 24.x). This is to prevent any mismatch in how the data coming from Engine is used by the offline player. To make sure everything is compatible, you will want to build a process for syncing any existing progress data and downloading an updated player configuration for each of the locally stored registrations before using the new version of the player on the device.
Getting Content Files From Engine
To download the course package for offline, you can use the API endpoint at /courses/{courseId}/zip?exportType=OFFLINE. This will return the ZIP file containing all of the course files stored in Engine for playback on the device.
The first time a course is requested will take a little longer as we gather the files for the one-time ZIP creation. All subsequent calls for this course will download the same ZIP unless the files have been changed since the initial creation.
Note: You must have the configuration setting PlayerIsAvailableOffline set to true for all courses you want to be available through this endpoint. You can configure this on a per-course basis through the API, or if you want all courses to be accessible, then you can configure this at the system level through the API or add this setting to your configuration file.
Here is an example of this call:
Get ZIP of latest version:
curl --location --request GET 'http://[your-engine-instance]/api/v2/courses/[courseId]/zip?exportType=OFFLINE' \
--header 'engineTenantName: default' \
--header 'Authorization: Bearer [auth token]’
Get ZIP of specific version:
curl --location --request GET 'http://[your-engine-instance]/api/v2/courses/[courseId]/versions/[versionId]/zip?exportType=OFFLINE' \
--header 'engineTenantName: default' \
--header 'Authorization: Bearer [auth token]’
Getting Player Configuration From Engine
The player needs some information about the current state of the learner’s Registration in order to play back the content and record the results. This is all stored in our Player Configuration object and is loaded from the filesystem (configuration.js) at the time of the course launch on the device.
The Player Configuration can be retrieved from the /player/configuration end point with the following JSON body example:
curl --location --request POST 'http://[your-engine-instance]/api/v2/player/configuration' \
--header 'engineTenantName: default' \
--header 'Authorization: Bearer [auth token]’ \
--header 'Content-Type: application/json' \
--data-raw '{
"registrationId": "[registrationId]",
"instance": 0,
"isTracking": true,
"forOffline": true,
"includeRegistration": true
}'
The result from this call's legacyData property should be stored on the device in a local database so it will be available later.
The path to the course on the device should be updated in the legacyData text when retrieving it from Engine. Engine provides a placeholder value ({rscpRelativePathToCourseFromPlayer}) in the text for easy string replacement.
This allows you to store the content in different places on the device depending upon the app framework and other requirements.
Launching the Offline Content
Before launching the content, you must pull the configuration data from the local database and write it out to the configuration.js file in the player directory. This is done at launch time so that any updates to the configuration data made during previous launches is reflected in the configuration.js file.
Next, to launch the content, simply point a web view in your app to the player directory’s modern.html page with parameters like this:
file://[path-to-player-files]/modern.html?configuration=&cc=en&cache=[engine-version]&playerConfUrl=configuration.js
This will open the player and tell it to use the configuration.js file to see where to find the content. This content path value was set earlier by updating the placeholder when getting the Player Configuration from Engine in the previous step.
If you have a blank page, or other errors in the webview, this typically means there is an issue with the configuration.js file in the player directory and you should check it for valid data.
Gathering the Results
When the SCORM course is properly exited, the player will navigate to a blank offline-results.html page in the player directory. This page is the key to getting the data for the next launch and syncing with Engine. The LocalStorage of this page will contain two items: configurationJs contains the updated Player Configuration data that you need to launch again, and the runtimeXml is the data that you need to sync back with Engine.
Your application should extract this data from the LocalStorage and store it with the proper keys in your local database on the device for later use.
Syncing With Engine
You use the /player/results endpoint to send the runtimeXML back to Engine for processing. Here is an example of the JSON body for this POST:
curl --location --request POST 'https://[your.engine.instance]/api/v2/player/results' \ --header 'engineTenantName: default' \
--header 'Authorization: Bearer [auth token]' \
--header 'Content-Type: application/json' \
--data-raw '{
"registrationId":"[registrationId]",
"isExitPlayer":true,
"legacyData":"[runtimeXml example:<?xml version=\"1.0\"?><RTD ... </RTD>]",
"instance":0
}'
IMPORTANT: Once you sync with Engine, you MUST retrieve the Player Configuration again to avoid Launch History conflicts on future syncs. You cannot use the same Player Configuration from before the sync with Engine once the data is saved. Getting a new Player Configuration after a successful sync will ensure a new Launch History ID is created and available for your next sync. You will need to replace the placeholder for the content path as well before saving to the database, as noted earlier.
Checking Current Registration State on Engine
If you need to get further results from Engine at a later date, then you will need to call the /api/v2/registration/{registrationId} endpoint in Engine while passing the proper registration ID. You may want to use this within your sync process to make sure you do not overwrite a more recent status from the learner launching the registration online.
Simple Database Schema for the App
At a minimum, you will need to track these data items in your application's database:
OfflineCourseState
- CourseId (the CourseId associated with the course in Engine)
- Version (the version of the course in Engine)
- RegistrationId (the pre-existing RegistrationId associated with the learner's attempt in Engine)
- PlayerConfiguration (the js string response in
LegacyDatafrom the/player/configrationendpoint for the Registration, initially, and theconfigurationJsvalue from LocalStorage after launching and exiting the content on the device) - RuntimeXml (the
runtimeXmlvalue from LocalStorage after exiting the content) - SyncDate (when this record was last synced with Engine)
- LastUpdate (when this record was last updated with PlayerConfiguration or RuntimeXml)
Sample App Code
We can provide an example application for iOS, Android, or Xamarin (C#) upon request. This code is only meant to give an idea of how an application might work with the different API endpoints mentioned above. It is not production-ready code that you would copy directly into your own application.
Server-Side Sample Code
Our sample code package includes a server-side example of how to get API tokens for your device and some example course list data to send so the device has what it needs to operate. This example is very simple and uses the HAPI framework to quickly create a REST API for the device to connect with.
This part can be handled in any way you like and this is just an example that we use for testing and development purposes.
Using Your Server Application As Middleware
Many customers have selected not to have their mobile apps connect directly to Engine, and have instead opted to create custom APIs that the mobile app uses with the main app server that it uses for everything else. This main app server would communicate with Engine and handle all of those processes.
This does simplify things on the device quite a bit and can be helpful in ensuring the mobile app gets the latest Player Configuration for all downloaded courses before going offline. This would typically be done at the same time the app connects to get the available content for a learner.
In order to do this, you must implement all of the previously mentioned API resources on your API, so the values can be passed to your server and then relayed to Engine.