Introduction
The Docebo embedded Learning Mobile SDK allows you to integrate an instance of your Docebo platform in your iOS, Android or React Native mobile application.
The Docebo platform instance opens when the user clicks on a button or interacts with an element - such as a link, or a string - in your mobile application. When embedded learning opens, the platform instance shows the training content you, as the Superadmin, have selected for the user based on the action the user is performing, in order to provide the best learning on the fly experience, enriching it with ad hoc training.
Depending on the embedded learning configuration, users can be automatically provisioned so that their learning in the flow of work is not an event interrupted by the need to log in.
This document is a technical guide on how to embed the Embedded learning into your mobile application.
Activating embedded learning
To activate embedded learning, reach out to Docebo via the Help Center, or by contacting your Account Manager (if your plan includes this option).
Prerequisites
Since embedded learning works in a web environment, it is necessary to host the necessary code in an external web platform; this content will then be shown to the user by opening a webview from the mobile client.
To implement your web platform you can follow the steps indicated in the dedicated knowledgebase articles:
In this example, we will show how to implement a basic web page, how to pass the data from the mobile client to the page and how to show the result to the user.
First steps
Setting up the web platform
For the sake of this example, we will separate the HTML and JavaScript code in two files; index.html
will hold our presentation code while index.js
will initialize the embedded learning Launcher or embedded learning Building Blocks.
Create the HTML
Start by embedding the necessary code to load the kernel in the <head>
element of your HTML; follow the steps listed here to embed the embedded learning app launcher code into your web page.
Step 1: Create the HTML
Start by embedding the necessary code to load the SDK in the head element of your HTML as specified above. To prevent the user from accidentally zooming the webview while browsing the launcher content from the mobile app, we add a viewport meta tag to disable that behaviour and to have the correct zoom level when loading the page. For more information refer to the Viewport meta tag MDN documentation (opens in a new tab).
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
Step 2: Link your JavaScript
Load the custom script file as the last element of the body tag of your HTML. This will make sure that the SDK has been loaded before calling any custom script.
<script src="./index.js"></script>
Step 3: Prepare the JavaScript
The first thing we want to do is to create the proxy that will handle the promise based communication between the mobile app and the JavaScript page. This will allow us to call a native method to retrieve the access token and wait for its response in the web page.
This solution is inspired by the react-native-webview-bridge-seamless example repository (opens in a new tab).
const handler = {
get() {
return () =>
new Promise(resolve => {
window.addEventListener(
"message",
function _listener({ data }) {
window.removeEventListener("message", _listener, false);
resolve(data);
},
false,
);
// NOTE: as the postMessage implementation is different for each platform
// we will inject this function from the native side before initializing the launcher.
// For example, React Native uses the `window.ReactNativeWebView.postMessage` global object
// whereas iOS uses `window.webkit.messageHandlers.<handlerName>.postMessage`
requestAccessToken();
});
},
};
window.getNativeAccessTokenProxy = () => new Proxy({}, handler);
// This is the function that will be passed to the Launcher or Kernel instance
async function renewToken() {
// The proxy will post the message requesting the token to the native code and resolve
// when the new token is available
// As it changes based on the platform, it will be injected by the native code upon start
return await window.getNativeAccessTokenProxy().get();
}
As you can see in the code comments, the requestAccessToken
will not be defined as it will be injected by the native code before the webview loads; this is necessary because the native postMessage
implementation varies from platform to platform. The function will post a message to the native side requesting the token; the request will then be intercepted and the token will be provided by a regular postMessage
by the native app.
At this point, the message will be intercepted by the listener that we defined in the proxy, and the promise will resolve with the new token.
Please note:
- The communication from JavaScript to native happens with the custom
postMessage
implementation, but the communication from native to JavaScript must use the regular windowpostMessage
.- In this example we are using JWT authentication as it is the preferred and most secure method, but you can use whatever authentication method you prefer. Refer to the dedicated articles Embedding the embedded learning Launcher into your webpage or embedded learning Building Blocks for further information.
Initialization
When the proxy is set up, the last step is to create a connect function that will be called from the native app once the webview is ready. The function will parse the received parameters and instantiate the launcher or Building Blocks kernel.
Best practice: It is advised that you place a loader in the HTML page so that the user will see it until the launcher or Building Blocks kernel is ready.
Embedded learning launcher
// Create a global instance of the launcher
const DFlowLauncher = DFlow.launcher();
// This function will be invoked from the native code as soon as the webview opens
function connect(options) {
const { domain, accessToken, language, launcherCode } = JSON.parse(options);
DFlowLauncher.setTokenProvider(renewToken);
DFlowLauncher.start({
domain,
launcherCode,
token: accessToken,
language,
})
.then(DFlowLauncher.open); // Open the launcher as soon as the launcher is ready
}
Tip: We are calling the DFlowLauncher.open()
function immediately after the connection has been established in order to expand the popup as soon as the launcher is ready; this step is completely optional.
Embedded learning Building Blocks kernel
// Create a global instance of the kernel
const DFlowKernel = DSDK.kernel();
// This function will be invoked from the native code as soon as the webview opens
function connect(options) {
const { domain, accessToken, language, context } = JSON.parse(options);
DFlowKernel.setTokenProvider(renewToken);
DFlowKernel.start({
domain: domain,
context: context,
token: accessToken,
language: language,
})
}