Build JavaScript Actions for Native Mobile

Last modified: February 7, 2024

1 Introduction

Native mobile applications have faster performance and give you the ability to use device hardware features. This tutorial teaches you how to build your own JavaScript actions, specifically ones that harness your native mobile device’s hardware to read Near Field Communication (NFC) tags. If you would like to use third-party modules other than an NFC scanner, refer to this tutorial as a general overview.

This how-to teaches you how to do the following:

  • Build a JavaScript action for a native mobile app
  • Add a dependency to the native JavaScript action
  • Implement an NFC scanner in a JavaScript action

2 Prerequisites

Before starting this how-to, make sure you have completed the following prerequisites:

  • Install Mendix 8.6.0 (Build 715) from the Marketplace
  • Install the Git command line tool
  • Install npm’s node js
  • Install Native Builder v3.1.0 (this tutorial can only be completed using Native Builder v3.1.0 or higher)
  • Have a fiscal NFC NDEF tag for testing
  • Own a mobile device with NFC capabilities

3 Building NFC JavaScript Actions

To build NFC JavaScript actions, you will do the following:

  1. Create a Mendix project.
  2. Build a native mobile app.
  3. Add a native dependency.
  4. Make two NFC JavaScript actions.
  5. Implement the NFC Scanner in your app.

3.1 Creating a Mendix Project

Follow these instructions to set up your NFC project:

  1. Open Mendix Studio Pro.
  2. Select File > New Project.
  3. Select the Blank Native Mobile App (also available online here).
  4. Click Use this starting point.
  5. Name your app NativeNFC and click Create App to close the dialog box.
  6. Rename module NativeMobile to NativeNFC. You will add your implementation In this module.
  7. Right-click the module and select Add other > JavaScript action. Name the new JavaScript action HasNFCSupport. You will create the implementation later.
  8. Open the Home_Native page and add some welcome text for you test app.
  9. Add an action button with caption Scan NFC Tag on your home page.
    1. Right-click your home page and click Add widget.
    2. Select Call nanoflow button.
    3. Click new.
    4. Set the Name to ACT_ReadNFCTag.
    5. Change the button’s caption to Read NFC Tag.
    6. Save the page.
    7. Open ACT_ReadNFCTag.
    8. Drag the HasNFCSupport JavaScript action onto this nanoflow.
    9. Save your nanoflow.
  10. Click Run to deploy to the Free App environment.

Your Mendix project should looks something like this:

native nfc app home

3.2 Building a Native Custom Developer App

When developing a native mobile app, you can use the Make it Native app to quickly get started. However, this app is bundled with a limited number of functionalities. This tutorial’s app requires an NFC module in order to access the native NFC capabilities. This can only be achieved by using the Native Builder. The Native Builder can create a custom developer app which you use to see and test your app.

In this section, you will create a normal custom developer app to learn the process. In the subsequent section Installing a Dependency in Your Custom Developer App you will create a tailored custom developer app suited for NFC tasks.

Now you will build a native custom developer app and install it on your phone:

  1. If you do not have your GitHub and App Center keys, follow Getting Your Tokens to get your authentication codes.

  2. Complete the Preparing Your Project section of How to Build a Mendix Native App in the Cloud using these parameters:

    1
    
    native-builder.exe prepare --java-home "C:\Program Files\AdoptOpenJDK\jdk-11.0.3.7-hotspot" --mxbuild-path "C:\Program Files\Mendix\8.6.0.715\modeler\mxbuild.exe" --project-path "Y:\MendixProjects\NativeNFC\NativeNFC.mpr" --github-access-token "a1f422..." --appcenter-api-token "a1b95a..." --project-name "Native NFC App" --app-name "Native NFC App" --app-identifier "com.mendix.howto.nativenfc" --runtime-url "https://nativenfc-sandbox.mxapps.io/"
    

    As a result of the prepare command your app is created in App Center:

    App Center apps

    A new repository is created in your GitHub account:

    Github template repo
  3. To build the app for the first time, complete the Build Your Developer App section of Create a Custom Developer App using this build command:

    native-builder.exe build dev-app --project-name "Native NFC App"
    

    The project name should match the project names from your prepare command.

Your apps are now available in the C:\native-builder\builds folder, where you will findNative-NFC-App-Android-1.zipfor an Android app andNative-NFC-App-iOS-1.zip for an iOS app.

  1. Install the app on your device. For more information on app installation, see the Distributing section of How to Build a Mendix Native App in the Cloud.
  2. Open your app on your testing device.
  3. Tap your NFC button. You will incur this error: JavaScript action was not implemented. You will implement your action later.

3.3 Installing a Dependency in Your Custom Developer App

In this section you will install the external library react-native-nfc-manager which makes adding NFC functionality easier. Mendix Studio Pro 8.6 is based on React Native version 0.59, which will influence the selected version of the library. You will make all changes to the main branch, because with each build a new branch is created (build/{number}) from main with your latest changes.

The dependency is split into two parts: the native operating system part and the client JavaScript part. To add the dependency for the app, do the following:

  1. Open a command line interface (CLI) and change directory to your GitHub project folder: cd C:/github/.
  2. Use your repository URL to clone the files on your machine with the command git clone https://github.com/user-name/native-nfc-app.
  3. Open the folder containing your cloned code: cd native-nfc-app.
  4. To install all its current dependencies, use the command npm install.
  5. Install the required library as a dependency with the command npm install --save react-native-nfc-manager@1.2.2. Note the version after the @ sign. Versions 2 and higher are not supported on Mendix Studio Pro 8.6.

Next you will use the react-native link command to link your module. This command works for React Native versions below 0.60. It is replaced in versions 0.60 and higher with auto-linking. Auto-linking does not require any linking or code changes. However, both processes have downsides and not every module supports them.

To integrate the module into the template, you will need to make a few code changes. Using link you can do most changes automatically with the React Native CLI. If you wish to link automatically, follow the Setting Up Automatic Linking section below. If you wish to link manually, see the Setting Up Manual Linking section below.

Regardless of which linking method you choose, complete the following steps first:

  1. Install the React Native CLI using the command npm install -g react-native-cli@2.0.1 (note that the versions should match the React Native version of your Mendix version).
  2. Link the new module with the command react-native link react-native-nfc-manager.

3.3.1 Setting Up Automatic Linking

You should see successes from the previous linking commands. Even when your linking shows successes in your CLI, linking problems may have occurred with your linked apps. To truly validate success, complete the following steps:

  1. Open C:\github\native-nfc-app\android\app\src\main\java\com\mendix\nativetemplate\MainApplication.java.
  2. Make sure the following is included in the list of imports: import community.revteltech.nfc.NfcManagerPackage;.

You can skip the Manual Linking section and move on to the Using the Modules section now.

3.3.2 Setting Up Manual Linking

This section shows how to link manually. This method replaces the linking steps in the Installing a Dependency in Your App section above and could be used for validating if the react-native link command succeeded.

To link for Android devices, do the following:

  1. To expose the library to the template, you must link it using Gradle. Open C:\github\native-nfc-app\android\settings.gradle, then add this line of code before include ':app' , ':mendixnative-release':

    1
    2
    
    include ':react-native-nfc-manager'
    project(':react-native-nfc-manager').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-nfc-manager/android')
    
  2. To use the new gradle module in C:\github\native-nfc-app\android\app\build.gradle, add this to your list of dependencies:

    implementation project(":react-native-nfc-manager")
    
  3. Now the main application needs to initialize the NFC manager. In C:\github\native-nfc-app\android\app\src\main\java\com\mendix\nativetemplate\MainApplication.java add an item to the Arrays.<ReactPackage>asList, add a comma after the other dependency, and add the new NfcManagerPackage():

    new NfcManagerPackage()
    
  4. When you add this file in Android Studio, the import is automatically added. If you use a plain text editor, add this import to your other imports: import community.revteltech.nfc.NfcManagerPackage;.

To link for iOS devices, do the following:

  1. Open C:\github\native-nfc-app\ios\Podfile.
  2. In the abstract_target section; before end, add a new line with pod 'react-native-nfc-manager', :path => '../node_modules/react-native-nfc-manager'.

For more information about linking, see the following resources:

3.3.3 Using the Modules

For Android devices, you must add code which checks if a user permits the module. Do this by adding uses permission in the android/app/src/main/AndroidManifest.xml file (specifically in the section above the <application line):

1
2
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />

For iOS you have to add permission to use NFC capabilities:

  1. Open your project in xCode.

  2. Select your project in the left menu.

  3. In Signing & Capabilities, click Capability and select Near Field Communication Tag Reading

    ios capabilities
  4. Add a usage description so the user can give their permission to use the NFC reader.

    1. In ios/nativeTemplate/Info.plist right-click.
    2. Select Add Row.
    3. Title the key NFCReaderUsageDescription with the description: To be able to read NFC tags, please accept.

3.3.4 Committing to the Template

For both platforms, you need to store your changes in the template. Push your changes to GitHub (where the Native Builder can use them later):

  1. To stage the changes, use the command git add . while in C:\github\native-nfc-app. You can check the staged files with the command git status.
  2. Commit the files with the command git commit -m "Add NFC Manager dependency".
  3. Now the files are committed, but they are only stored locally on your machine. Push them to your repository with the command git push. This will make the changes available so that the Native Builder can create a new app with NFC support.

3.4 Installing a Dependency in Your Project

The dependency is split into two parts: the native device part, and the client JavaScript part. In this section we will add the dependency JavaScript for the client bundle. For the bundling we need add the dependency builder can add the react-native-nfc-manager JavaScript code.

  1. In your CLI, open the module folder which contains your JavaScript action:

    cd C:\MendixProjects\NativeNFC\javascriptsource\nativenfc\actions
    
  2. Make sure HasNFCSupport.js is in this folder so you know you are in the right place.

  3. Install the dependency with the command npm install react-native-nfc-manager@1.2.2.

3.5 Creating NFC JavaScript Actions

JavaScript actions for web and native platforms are similar. However, they have their own set of dependencies which they can build on.

Build an action to check if a device supports NFC:

  1. Open the HasNFCSupport JavaScript action.

  2. Change the Return type to Boolean.

  3. Add this import above the EXTRA CODE block:

    1
    2
    3
    
    import { Big } from "big.js";
    import { NativeModules } from "react-native";
    import NfcManager from "react-native-nfc-manager";
    
  4. Replace the content of the USER CODE block with the following:

    1
    2
    3
    4
    
    if (!NativeModules.NfcManager) {
        throw new Error("The NfcManager module is not available in your app.");
    }
    return NfcManager.isSupported();
    

    Explaining the code:

    • The NativeModules contains all loaded modules. This allows you to check if the app has the module installed. This will throw an error when the action is used in the Make it Native app.
    • The NfcManager is imported from your newly added module. The isSupported functions check if NFC is supported by the hardware. They return a Promise that will resolved to a Boolean value to indicate if NFC is supported.

    When finished, your code will look like this:

    has NFC support action code
  5. Optionally, click the Expose as nanoflow action tab, select Expose as nanoflow action, and Select an icon for your JavaScript action.

Now make an JavaScript action to read the NFC tag information:

  1. Create a JavaScript action named ReadNFCTag.

  2. Select Return type > String.

  3. Click the Code tab, and add the import above the EXTRA CODE block:

    import NfcManager, { Ndef } from "react-native-nfc-manager";
    
  4. Add the following code to the USER CODE block:

    1
    2
    3
    4
    5
    6
    7
    
    return new Promise(resolve => {
        NfcManager.registerTagEvent(tag => {
            NfcManager.unregisterTagEvent();
            const text = Ndef.text.decodePayload(tag.ndefMessage[0].payload);
            resolve(text);
        });
    });
    

    Explaining the code:

    Here you return a promise that resolves a string value. The nanoflow will wait until the resolve function is called. The registration listens for tags that are picked up by the reader. When the callback function is executed as a tag is found, un-register the listener to stop listening for other tags. The payload is decoded from a byte array into text. When the resolve function is called with the text parameter, that nanoflow will receive this value as the return parameter.

    When finished, your code will look like this:

    Read NFC tag action code
  5. Optionally, click the Expose as nanoflow action tab, select Expose as nanoflow action, and Select an icon for your JavaScript action.

3.6 Using NFC JavaScript Actions

Make a nanoflow to use your new actions:

Scan tag nanoflow

To make the nanoflow shown above, do the following:

  1. Open ATC_ReadNFCTag.
  2. Double-click the Has NFC Support action, set the Variable name as HasNFCSupport, and click OK.
  3. Right-click the Has NFC Support action, select Set error handling, and set the type to Custom without rollback.
  4. Create a Show message action, set the type as Error, and set the template as: Error occurred while checking NFC support: {1}. Add a parameter containing $latestError.
  5. Connect the Has NFC Support activity to the Show message activity. Right-click the connection and select Set as error handler.
  6. Add an end event under your error message, then connect the message to the end event.
  7. Add a Decision action. In its Expression check for the return variable with the expression $HasNFCSupport, write Has NFC support? in Caption, then click OK. Add an end event under this show message activity.
  8. If a device is not supported, show a message of type warning. Create a Show message action with template text Sorry, your device does not support NFC. and then connect this error message to the decision.
  9. If a device is supported, add the Read NFC Tag action and store the response in the variable TagValue.
  10. Set the sequence flows from the decision to True (going left) and False (going down).
  11. Right-click the Read NFC Tag action and select Set error handling. Set the type to Custom without rollback.
  12. Create a Show message action, set the type as error, and set the template text to Error occurred while reading an NFC tag: {1}. Use $lastError as the single parameter.
  13. Connect the Read NFC Tag activity with a Show message activity. Right-click it, and select Set as error handler.
  14. Connect this Show message action to an end point.
  15. Create a Show message action, set the type as information, and set the template as Your NFC tags says: {1}. Use $TagValue as a parameter.
  16. Optionally you can add Show progress and Hide progress activities to give your user more information while using the NFC reader. This action can be found in the Nanoflow Commons module.
  17. Deploy your app to the sandbox.

3.7 Writing an NFC Tag

Now you have a way to read NFC NDEF tags. Next you will write some text for your tag. You can create a JavaScript action for this yourself or use an existing tool. If you use an existing tool, Mendix recommends NFC Tools Android or NFC Tools iOS.

To write your own NFC tag, do the following:

  1. Install the NFC Tools app on your device.

  2. Open the NFC Tools app.

  3. Scan your tag. The Technologies available section should state it supports Ndef. The Writeable section should show Yes.

  4. Tap WRITE, tap Add a record, and tap Text.

  5. Enter the text Hello Mendix Developer! and tap OK.

  6. Tap Write / 30 Bytes.

  7. Scan your tag. You will see a Write complete dialog box:

    write nfc tag

3.8 Rebuilding Your Native Mobile App

Now that you added NFC capability to your app’s source code, you must rebuild your native mobile app and reinstall it on your device to use the new JavaScript actions.

  1. Open your CLI.

  2. Run the following command to rebuild your NFC app:

    native-builder.exe build dev-app --project-name "Native NFC App"
    

    This builder will use the configuration you set during the Install a Dependency in Your Project section above.

  3. After the build has successfully finished, the build file will be available in C:\native-builder\builds.

  4. Uninstall the previous version of the app on your device.

  5. Install the app on your device. For more information on installing an app on your device, see the Distributing section of Build a Mendix Native App in the Cloud.

  6. Open the app, tap Scan tag, and scan your NFC tag. You should see a dialog box with the text you assigned to your tag:

    read NFC successfully

Congratulations for completing this NFC tutorial! To go beyond the lessons you have learned, see the sections below.

3.9 Hardening the Code

Now you have a working NFC scanner. However, you can improve it for both Android and iOS.

On Android — NFC scanning can be switched off. Also, scanning should be canceled if the back button is clicked.

On iOS — Scanning can be canceled when the Ready to Scan dialog box is up.

To implement these capabilities, replace all the USER CODE in the ReadNFCTag JavaScript action with the following code, then repeat the steps in the Rebuilding Your App section above to build and install the updated app on your device:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import { Big } from "big.js";
import { BackHandler, NativeModules, Platform } from "react-native";
import NfcManager, { Ndef } from "react-native-nfc-manager";

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
 * @returns {Promise.<string>}
 */
export async function ReadNFCTag() {
    // BEGIN USER CODE
    if (!NativeModules.NfcManager) {
        throw new Error("The NfcManager module is not available in your app.");
    }
    if (Platform.OS === "android") {
        const enabled = await NfcManager.isEnabled();
        if (!enabled) {
            throw new Error("NFC is not enabled");
        }
    }

    return new Promise(async(resolve, reject) => {
        let success = false;
        await NfcManager.start({
            onSessionClosedIOS: () => {
                if (!success) {
                    reject(new Error("NFC session closed"));
                }
            }
        });
        if (Platform.OS === "android") {
            BackHandler.addEventListener("hardwareBackPress", async () => {
                await NfcManager.unregisterTagEvent();
                await NfcManager.stop();
                return reject(new Error("NFC was canceled by the user"));
            });
            NfcManager.onStateChanged(
                async event => {
                    if (event.state === "off" || event.state === "turning_off") {
                        await NfcManager.unregisterTagEvent();
                        await NfcManager.stop();
                        return reject(new Error("NFC was disabled by the user"));
                    }
                }
            )
        }
        NfcManager.registerTagEvent(async tag => {
            success = true;
            await NfcManager.unregisterTagEvent();
            await NfcManager.stop();
            const text = Ndef.text.decodePayload(tag.ndefMessage[0].payload);
            resolve(text);
        }, "Read NFC");
    });
    // END USER CODE
}

Explaining the code:

At the beginning of the action on Android, the code checks if the NFC tag reader is switched off and throws an error if so. It creates a Promise with resolve and reject parameters. Note the async keyword before the function. This allows await to be used with an asynchronous function and lets them execute together while respecting their order in the code. The start will initialize the module and register a callback for iOS. This callback will be called when the NFC NDEF reader session becomes invalid, either because of the OS or because the Cancel button was tapped.

For Android, a listener for the hardware back button is included. When you tap it, you will stop listening for tags, and cancel the execution by calling the reject function. This way the nanoflow will receive an error that is caught by the error handler.

When the app is listening for a tag, you can switch off the NFC function in Android. This causes a state change that you will catch, and causes a rejection to the promise.

The second parameter of the registerTagEvent function is the instruction text which appears in the iOS Ready to Scan dialog box. After the tag is found by the reader, you have to stop the NFC manager. This way you stop listening for state changes on Android, and you stop listening for the session to close in iOS.

Congratulations on making your own native JavaScript action! Please use your own creativity to extend the NFC functionality or implement other modules with the lessons you have learned.

4 Building for Release

Until this section, you have used a custom developer app to validate your application. When you want to distribute your app in the Google Play Store or Apple App Store, you have to make a build that includes the bundled Mendix app.

For the full explanation on how to build, sign, and distribute your app to an app store see Deploy Your First Mendix Native Mobile App with the Native Builder CLI as well as the tutorial’s subsequent sections.

5 Read More