Skip to content
  • There are no suggestions because the search field is empty.

Creating a mobile application using Cordova

how to set up the mobile application using Cordova and integrate it with Betty Blocks.

Introduction

This guide provides instructions on how to set up the mobile application using Cordova and integrate it with Betty Blocks. It focuses on the custom code required for this project while referencing the official Cordova documentation where needed.

Setup

The setup process includes installing necessary dependencies via npm, configuring Cordova settings in config.xml, and managing platform-specific configurations for Android and iOS.

Cordova Prerequisites

  • Min. XCODE
    • Version 16.1 (16B40)
  • XCODE Development Tools
  • XCODE IOS SDK
  • Latest version of: Android Studio Ladybug | 2024.2.1 Patch 1
    • Runtime version: “21.0.3+-79915917-b509.11 aarch64” or newer
    • VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
  • Java 17 or openjdk@17.0.0
  • Min. Android 14.0 SDK (API 34)
  • Min. Android SDK Build-Tools 34.0.0
  • Latest version: Cordova CLI
  • Latest version supported by cordova: Node 23.0.0

Our advice is to install all the CLI tools, such as the cordova CLI, Node and java (or openjdk) with Brew. Brew is a package manager that makes version management a lot less complicated and also directly sets all the correct symlinks.


If any other version is used than indicated in the above list, the code might not work. Whenever issues arise, first make sure to check if you have the correct version installed. 

Features

  • Integration with OneSignal for push notifications

  • Handling local storage within the app

  • Injecting an iframe to interact with Betty Blocks

Build instructions

Instructions are provided for compiling the app for different platforms and using emulators for testing. The guide also includes manual steps for installing iOS dependencies using CocoaPods.

Custom Cordova hooks

The project includes custom Cordova hooks to automate tasks such as modifying the Podfile and installing dependencies.

Custom Betty Blocks components

Custom components are used to facilitate communication between the web app and the Cordova wrapper. The guide explains how events are triggered, processed, and exchanged using the postMessage API to enable interactions like navigation, token storage, and push notification requests.

 

 

This document focuses solely on the custom code written specifically for this application. Topics already covered in the official Cordova documentation will not be explained here. Wherever applicable, links to relevant Cordova documentation will be provided for further reference.

 


Initial setup

  1. Extract the compressed .zip.
  2. Navigate into the folder 

cd ./organisation-folder

  1. Use   npm install  to install the node dependencies.

Extending existing capabilities

If you want to update the existing capabilities of the application, you will need to edit the files in the  /www  folder. 


Config.xml

From this config file Cordova builds the different platforms. You can define different settings or plugins for each platform or create global configurations. Please read more at: https://cordova.apache.org/docs/en/12.x/config_ref/index.html


Index.html

The  index.html  is the starting point of the application and this is where the iframe is rendered. It is important to note that the iframe is injected programmatically. This is done to make it easier to manipulate the iframe and set up the  onLoad()  event listener. 


Index.js

The  index.js  is the entry point for all functional code. Each section has been explained below:

class OneSignal {
  // For official documentation on the OneSignal SDK, see:  https://documentation.onesignal.com/docs/mobile-sdk-reference

  static init(logLevel = 0) {
    // Initializes the OneSignal integration for both iOS and Android
    // The loglevel can be used for debugging:
    // LogLevel: NONE: 0 | FATAL: 1 | ERROR: 2 | WARN: 3 | INFO: 4 | 
    // DEBUG: 5 | VERBOSE: 6
  }

  static async getID() {
    // Returns the unique OneSignal ID for the user
  }

  static async optIn() {
    // If the user has accepted push permissions, but has turned off the 
    // notification for the app in their settings, this will show a 
    // native window. See more: 
https://documentation.onesignal.com/docs/mobile-sdk-reference#optin
  }

  
static async getOptedIn() {
    // Can be used to get a boolean value indicating if the user has opted 
    // in to receive permissions. This value is different then the value 
    // received when using the requestPushPermission(). See more at: https://documentation.onesignal.com/docs/mobile-sdk-reference#optedin

  }

  static async requestPushPermission() {
    // Is used when to display the “Accept push notifications” prompt. 
    // Returns a boolean value indicating whether the user has accepted or 
    // not. See more at: https://documentation.onesignal.com/docs/mobile-sdk-reference#requestpermission-push

  }
}
class DeviceStorage {
  // This class is used to read/write items from/into the local storage of 
  // the phone. This does NOT read the local storage from the iframe or 
  // browser.
  static getItem(key) {
    // Gets the value stored in the localStorage. 
  }

  static setItem(key, value) {
    // Sets the passed value. Automatically stringifies objects if passed.
  }
}
function initIframe() {
  // Injects the Iframe into the body and sets the ‘src’ attribute to the 
  // value of the constant variable APP_URL
}



function initLocalStorage() {
  // Initializes the localStorage by reading the TOKEN and REFRESH_TOKEN 
  // from the device’s memory. Then fires an event to be captured by the 
  // eventListener component in the Betty Blocks application.
}

 

function handlePostMessage(msg) {
  // Generic function used to communicate from Cordova to BB via events
}

Build instructions

Every time a change is made to any of the files in the  /www  folder, the app needs to be rebuilt.


  1. Navigate to the Cordova App folder.

cd ./organisation-folder/

  1. Use the build command to build the apps for all platforms

cordova build

If you wish to make a build for a specific platform, you can use  cordova build <platform> . For example:  cordova build android .

IOS emulation

  1. Make sure your  cwd  is named accordingly, for example organisation-folder.
  2. Open the XCODE workspace  .xcworkspace .

open platforms/ios/Digital\ appname\ Tracker.xcworkspace

  1. After XCODE has loaded, press the play button to start the emulator.

After pressing the play button, XCODE will compile the app and open up an emulator. If you are prompted to select an emulator, please make sure to select Iphone 16 Pro.


For more information on iOS emulation, please refer to

https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device/

(Re-)installing Pods

Follow the instructions below if you need to manually install the pods (dependencies) for iOS. A hook has been set-up to take care of this for you when you run cordova build, but these steps explain the manual process.


  1. Inside XCODE, open the Pods folder.
  2. Open the Pod-file.
  3. Check if the contents of the Pod-file are equal to that of the podfile-backup.txt. If not, copy and paste the contents of the backup file into XCODE's Podfile
  4. Go back to your terminal and navigate to the /platform/ios folder.
  5. Use the command

pod install --repo-update


Android emulation

  1. Open Android Studio.
  2. Choose to open an existing project.
  3. Select the folder ./organisation-folder/platforms/android

and press ‘Open’.

  1. Wait until the project has finished loading.
  2. Press the play button.

After pressing the play button, Android Studio will compile the app and open up an emulator. If you are prompted to select an emulator, please make sure to select Pixel 9 Pro API 34.


For more information on android emulation, please refer to

https://developer.android.com/studio/run/emulator

Cordova hooks

Cordova allows for hooks to be run after certain criteria have met in the build process. Hooks are useful functionalities that allow you to perform certain actions such as manipulating build files or installing dependencies.


Please see the official Cordova documentation on adding Hooks.


after_prepare

ios_add_onesignal_target.js: This hook takes care of adding extra targets to the Podfile. This is because the target OneSignalServiceExtension needs to be registered in the Podfile which does not happen out of the box with  cordova build . If any additional targets need to be registered or have dependencies added, then this file needs to be updated.


ios_install_pods.js: This hook takes care of installing the pods (dependencies) after the build phase has completed.

OneSignal Integration

Android

Android is already set up for deployment.

iOS

  1. Open the .xcworkspace with XCODE.
  2. Select the Target: “DLT”.
  3. Go to Signing & Capabilities.
  4. Next to the button + Capability, select “All”
  5. Make sure you have selected “Automatically manage signing” for Debug & Release builds.
  6. Select your Apple Developer Team.
  7. Make sure there is no warning and a Signing Certificate has been filled in for Debug and Release.

  1. Scroll down to “App Groups”. 
  2. Make sure the “App Groups” capability is present and that  “group.com.organisation.appname” is selected. If it isn’t present, use the ‘+’-symbol to add a new group. Make sure to name it group.com.organisation.appname
    NOTE: A group only needs to be created once between all targets! It might take some time to load the app groups.

  1. Make sure the “Push Notification” capability is present.
  2. Repeat the entire process for the other target “OneSignalNotificationServiceExtension”. 


Custom Components

To be able to support certain features, such as using the Betty Blocks application as a mobile app, custom components were created. 

Please note: this document does not provide information on how to create custom components, please refer to the official documentation at docs.bettyblocks.com.

List of custom components

  • Event Listener
  • Link Listener
  • Event Trigger
  • Custom Hidden Input
  • Custom Filter Button
  • CombineFilter
  • OneSignal PWA

 

Throughout this chapter, you will find illustrations and examples how of to use them.

Where to find the components?

The components can be created custom, or with help from the Betty Blocks team according to your contract. 

Event Listener

This component captures all events that can be triggered by either the Event Trigger component or by events that are triggered by the Cordova wrapper. 

Custom Events

This component will listen for the following custom events:

  • “navigate”
    • This event will handle any navigation requests that are coming from Cordova. Use this if you want to programmatically redirect a user.

handlePostMessage({
  type: "MOBILE",
  data: {
    type: "navigate",
    value: "/home",
  },
});
  • “token_exchange”
    • When the user has logged in, we have to store the TOKEN and REFRESH_TOKEN on the user’s device. This event is triggered by the Cordova wrapper, passing the TOKEN and REFRESH_TOKEN as values along with the event, to then inject the tokens in the iframe. 
handlePostMessage({
  type: "MOBILE",
  data: {
    type: "token_exchange",
    value: {
      token,
      refreshToken,    
    },
  },
});

 

If an event is triggered with an unknown type (event.data.data.type), this component will log the following error:

The event ${type} is not configured yet. Expand the switch statement in the eventListener component.

Please note that the previously mentioned events will not be executed if the option “Interaction” is set to True.

Please read more information about how events are handled in the chapter: Communicating via Events.

Component Options

Interaction

This will cause the component to skip the aforementioned custom events, but instead will trigger the set interaction. 


Debug

This can be turned on to debug the events that are captured. When in runtime, the component will display information as:

  • If the event was received 
  • The type of event that was triggered
  • The value that was passed with the event
  • An error message if present

How to use the Event Listener with interactions

If you have turned on the interaction option, you’ll need to define an interaction in the Interactions tab. Below is an example:


In this instance, Cordova uses the postMessage() function to trigger an event. This event is then captured by this component, and because the option Interaction is set to true, it will use the interaction defined in the interactions tab. Otherwise, it will execute the code as in the section Custom events.




You can find this example on the DLT application page: Register User


Link Listener

This component is specifically set up to listen for clicks on anything with an href attribute. Whenever a page includes links outside of the application, or URI schemes such as (but not limited to): mailto: or tel:, then this component needs to be present on that page. 

The link listener has a built in feature that will check if the userAgent (device accessing the page) is a mobile. If for some reason, the inAppBrowser plugin does not open but the app navigates to the page, then this condition needs to be expanded. Currently, we use the following regulate expression to test the userAgent:

const getIsMobile = () => {
  const { userAgent } = navigator;
  return /Mobi|Android|iPhone/i.test(userAgent);
}

 

This component exists because we need to be able to open the InAppBrowser plugin in the Cordova Wrapper. This component will only open the InAppBrowser if the app is being accessed from a mobile device, otherwise it will use the default behaviour (open link).

Options

The component has no options.

This component utilizes the Event Trigger’s interaction: “Trigger Event” to communicate to the Cordova wrapper that a link has been clicked. The Cordova wrapper will then respond by opening the inAppBrowser. An example can be found on the page “Register User”.

The example on the left is utilizing the Event Trigger even though it (Event Trigger) has its type set to “Request Push Permission”. The Event Trigger is set up so that it will just pass along the event from the Link Listener.


Please read more information about how events are handled in the chapter Communicating via Events.

Event Trigger

This component is used to communicate from the Betty Blocks application to the Cordova wrapper. The event trigger has been programmatically set up to be able to trigger events:

  • Login
  • Request OneSignal Push Permission
  • Request token exchange
  • Request OneSignal ID
  • Redirect

It is also possible to directly pass the events from the  Link Listener to the  Event Trigger so that Cordova can respond properly by opening the InAppBrowser.

Options

When to trigger

  • On Load: When the page loads
  • Delayed: After a defined delay
  • Interaction: Only triggers the event via an interaction

Delay

Delay in ms. Only present if When to trigger is set to ‘Delayed’.


Trigger Event

The type of event to trigger in Cordova.


Custom Value to pass with the event

Only present if “Trigger Event is set to ‘Redirect’.

Please read more information about how events are handled in the chapter: Communicating via Events.

Custom Hidden Input

This is an extension of the default Hidden Input provided by the platform. We have added the capability to set the value via an interaction called: “setValue”.

Options

Value

The value to pass to the action.


Action Input Variable

The action variable to pass this value to after the user has submitted the form.

Custom Filter Button

It fetches the data from the db according to the selected model and renders buttons/clickable badges based on the data in the database for the selected Filter Model. It is used to filter datatables/datalists. 

Options

Target model 

This model should be equal to the model that is set on the datatable/datalist you want to filter.


Filter model

This is the relational model you wish to filter on. In this example, a Contact belongs to an Office. The amount of buttons that will be rendered will be based on the data in the model Office.


Button Property

The text you wish to display in the button.


Overflow

Decide how to display the buttons. The options are:


  • Scroll: Renders the content at the maximum width of the container and automatically applies a scrollbar if needed.

  • Show: All buttons will be placed in the container, cascading if necessary.
   

OneSignal PWA

This component should only be used if you are deploying your app as a PWA and want to integrate push notifications. This component only ever needs to be placed on the pages where you want to ask users for permission to send push notifications.

Options

Content

Insert your OneSignal Client ID here.

Communicating via Events

To enable the application to function within a Cordova wrapper, we have implemented a system that facilitates bi-directional communication between the Betty Blocks application and the Cordova wrapper with the use of events.

This chapter will cover the following topics:

  1. Purpose of the implementation.
  2. Communication Flow
    1. From the Betty Block application to the Cordova Wrapper.
    2. From the Cordova Wrapper to the Betty Blocks application
  3. Types of Events handled in the 
    1. Cordova Wrapper
    2. Betty Blocks application

Purpose of this documentation

The Betty Blocks application is embedded within a Cordova-based mobile wrapper. This setup requires bidirectional communication to enable interactions between the web application (Betty Blocks) and native mobile capabilities exposed by the Cordova wrapper.

Since the web application cannot directly access native features like push notifications, token management, or system interactions (e.g. opening external links), this implementation bridges the gap. It ensures that:

  • The Betty Blocks app can request native features and data from the Cordova wrapper.
  • The Cordova wrapper can send events and responses back to the Betty Blocks application.

Communication flow

This chapter will help you understand how the Betty Blocks application and Cordova wrapper communicate. 

The postMessage API from the window object is used to send messages to the Cordova wrapper. See more information about this API at:
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

Utilizing the postMessage API for Event Communication

To enable seamless communication between the Betty Blocks application and the Cordova wrapper, we leverage the postMessage API. This API provides a secure and efficient mechanism for sending messages between a web page (in this case, the Betty Blocks application running in an iframe) and its parent container or other contexts, such as the Cordova wrapper.

How postMessage works

  1. From the Betty Blocks to Cordova:
    1. The Betty Blocks application uses window.parent.postMessage to send messages to the Cordova wrapper. It does this via the Event Trigger component.
    2. Each message includes a structured payload that defines the type of event ( type ) and any associated data ( value ).
    3. Example:
window.parent.postMessage(
  {
    type: "MESSAGE",
    data: {
      type: "login",
      value: { token, refreshToken },
    },
  },
  "*" // APP_URL
);
  1. From Cordova to Betty blocks
    1. The Cordova wrapper listens for events using the message event listener on the window object.
    2. Messages are processed and routed based on their type, triggering appropriate actions in the Betty Blocks application.
    3. The wrapper sends messages back to the iframe using the postMessage API. But, instead of using the window object, it uses the iframe itself:
iframe.contentWindow.postMessage(
  {
    type: "MOBILE",
    data: {
      type: "interaction",
      value: oneSignalID,
    },
  },
  "*" // APP_URL
);

Key benefits of postMessage

    • Cross-Origin Communication: The API works across different origins, making it ideal for scenarios where the Cordova wrapper and the application originate from different domains.
  • Event-Based Structure: The use of event types ensures that messages are routed and processed efficiently based on their purpose.
  • Asynchronous Execution: postMessage does not block the main thread, ensuring that communication does not hinder application performance.

By utilizing the postMessage API, we have established a robust and scalable foundation for managing interactions between the Betty Blocks application and the Cordova wrapper.

OneSignal Implementation

OneSignal is the provider of push notifications currently used in the applications:

  • Digital Legislation Tracker
  • Events Template

Official OneSignal Documentation

Betty Blocks Actions

All actions that are used to communicate with OneSignal, or do something related to OneSignal (e.g. update the WebUser’s OneSignal Identifier), have been prepended with [OneSignal] to easily identify them.

Some of our actions have extra “tags”. Please see more info on these tags in the section Dictionary - Action Tags.

[OneSignal] Update User’s Tags

In OneSignal, a user can have tags. These tags are used to identify users when push notifications are sent. 

To update a user’s tags, we have to utilize OneSignal’s endpoint: Update User.
https://api.onesignal.com/apps/<APP_ID>/users/by/onesignal_id/<ONESIGNAL_ID>

Read more information about how the OneSignal endpoint Update User works at: OneSignal: Update User.

The action takes care of fetching the topics and jurisdictions the user is subscribed to and inserts them into an object: 

{
  jurisdiction_1: true,
  jurisdiction_2: null,
  topic_1: true,
  topic_2: true,
  topic_3: null
}

Each key is appended with the identifier of the record and a value is set. The values can be set to either:

  • true: The user is subscribed to this topic
  • null: The user is not subscribed to this topic. OneSignal will identify this null value and remove the tag from the user if it is present. 

A PATCH request is then made to the OneSignal endpoint along with a request body that contains the aforementioned tags. 


[OneSignal][API] Create Push notification

Push notifications can be made from the back office of the Betty Blocks application. Because this is a different environment, we have to use the GraphQL Action API. You can read more on this type of implementation here: https://docs.bettyblocks.com/triggering-actions-via-an-api-call.

When a request for a new push notification is received, the action processes the selected jurisdictions and topics by converting each into an object that looks like this:

{
  field: “key”,
  key: “jurisdiction_1”,
  relation: “=”,
  value: true
}

Each object is then inserted into an array which is then used in the request to create a push notification in OneSignal. 

Please read more information about how filtering works in OneSignal at: OneSignal: Notification Filter.

Endpoints

We have utilized the following endpoints from OneSignal:

  1. Create Message → Push notification
    1. POST
    2. OneSignal Endpoint: /notifications?c=push
    3. Documentation 
  2. Update User (subscriptions)
    1. PATCH
    2. OneSignal Endpoint:
      /apps//users/by/onesignal_id/
    3. Documentation
  3. Delete User
    1. DELETE
    2. OneSignal Endpoint:
      /apps//users/by/onesignal_id/
    3. Documentation

Dictionary

Action Tags

[SUB]: This is an action that was primarily designed to be used as a sub-action.

[API]: This action can only be run via a POST request to the action’s endpoint. It’s primarily used to trigger Next-Gen functionality from the back office. Using this action as a sub-action or triggering it via the GQL Playground will not work. Read more at: docs.bettyblocks.com.

[SCHEDULED]: This is a scheduled action and will run automatically according to the configured scheduling. Read more at: docs.bettyblocks.com.

[OneSignal]: Primarily used to communicate with the API of OneSignal or manipulate the data relating to this API (e.g.: Updating the Webuser’s OneSignal Identifier)