Step-by-Step Guide to Creating a Barcode Reader App for Salesforce AppExchange

While using Salesforce, you can easily extend your management system’s functionalities by installing apps from Salesforce AppExchange, the Salesforce marketplace. But what if you want to build a Salesforce app yourself? In this article, I’ll guide you through creating a barcode reader app for Salesforce from scratch.

Prerequisites

Setting Up Your Salesforce Environment

Before diving into coding, you’ll need to configure your Salesforce account. Start by creating a Salesforce account.

Salesforce partner community

Upon signing in, you’ll notice two interfaces: the classic UI and the Lightning UI. You can switch between them at any time.

Next, connect to a developer edition org in Environment Hub, which you’ll use to build apps with a developer account.

Environment hub

Don’t forget to retrieve your security token from My Settings, as it’s required when connecting to different organizations within the Salesforce partner community.

Salesforce security token

Installing Development Tools

To develop Salesforce apps, you’ll need to install Salesforce CLI and the Visual Studio Code extensions.

Salesforce CLI

Install the Salesforce Command Line Interface (CLI) from Salesforce CLI Installation

sfdx command

Visual Studio Code Extensions

Search for “salesforce pack” in Visual Studio Code and install the necessary extensions.

Salesforce Visual Studio Code extension

Building Your Salesforce App

In Visual Studio Code, press Ctrl+Shift+P and type “sfdx: create project” to create a new project named BarcodeReader:

Salesforce create project

Edit the project-scratch-def.json file and change the orgName according to your Salesforce account details:

{
  "orgName": "Dynamsoft",
  "edition": "Developer",
  "features": [],
  "settings": {
    "lightningExperienceSettings": {
      "enableS1DesktopEnabled": true
    },
    "securitySettings": {
      "passwordPolicies": {
        "enableSetPasswordInApi": true
      }
    },
    "mobileSettings": {
      "enableS1EncryptedStoragePref2": false
    }
  }
}

Authorize your org before deploying the local project to it.

authorize org

Once authorized, the deployment option will be available in the context menu upon right-clicking.

Salesforce source deployment

Creating a Visualforce Page

Create a Visualforce page called DecodeFile using the SFDX command.

Visualforce page

Deploy the page to Salesforce, where you can view it online.

Visualforce page online

After setting up the Visualforce page, add the barcode decoding logic using the provided JavaScript code snippet.

<apex:page >
<html>

<head>
    <script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.6.42/dist/dbr.js"></script>
</head>

<body>
    <h1>Dynamsoft JavaScript Barcode SDK</h1>
    <p></p>
    <div id="performance"></div>
    <input type="file" id="barcode-file" onchange="loadfile()" accept=".jpg,.jpeg,.png,.bmp" />
    <div>
        <img id='image' />
    </div>

    <script>
        Dynamsoft.DBR.BarcodeReader.license = "LICENSE-KEY";
        var performanceReport = document.getElementById('performance');
        var barcodereader;
        (async () => {
            barcodereader = await Dynamsoft.DBR.BarcodeReader.createInstance();
        })();

        var ratio = 0.6;
        function loadfile() {
            let name = document.getElementById('barcode-file');
            var img = document.getElementById('image');

            var reader = new FileReader();
            reader.onload = function (evt) {
                img.onload = function () {
                    console.log(img.width, img.height);
                    // load image to canvas
                    var canvas = document.createElement('canvas');
                    if (img.width > 1000 || img.height > 1000) {
                        canvas.width = img.width * ratio;
                        canvas.height = img.height * ratio;
                    }
                    else {
                        canvas.width = img.width;
                        canvas.height = img.height;
                    }
                    var context = canvas.getContext('2d');
                    context.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
                    var buffer = context.getImageData(0, 0, canvas.width, canvas.height).data;
                    let decoding_start = Date.now();
                    barcodereader
                        .decodeBuffer(
                            buffer,
                            canvas.width,
                            canvas.height,
                            canvas.width * 4,
                            Dynamsoft.DBR.EnumImagePixelFormat.IPF_ARGB_8888
                        )
                        .then((results) => {
                            let decoding_end = Date.now();
                            performanceReport.innerHTML = "File name: " + name.files[0].name + ", time cost: " + (decoding_end - decoding_start) + " ms <br>";
                            console.log("File name: " + name.files[0].name + ", time cost: " + (decoding_end - decoding_start) + " ms");
                            let txts = [];
                            try {
                                let localization;
                                for (var i = 0; i < results.length; ++i) {
                                    performanceReport.innerHTML += "Result: " + results[i].barcodeText + " <br>"
                                    txts.push(results[i].barcodeText);
                                }
                                let out = txts.join(', ');
                                if (txts.length > 0) {
                                    console.log(out);
                                }
                                else {
                                    performanceReport.innerHTML += 'No barcode found'
                                    console.log('No barcode found');
                                }
                            } catch (e) {
                            }
                        });
                };
                img.src = evt.target.result;
            };
            reader.readAsDataURL(name.files[0]);
        };
    </script>
</body>

</html>
</apex:page>

Note: You need to replace LICENSE-KEY with your own.

Save your changes and re-deploy the code. Here’s a preview of the Visualforce page.

Salesforce barcode reader

You can also create another Visualforce page for decoding barcodes from a camera video stream and deploy it to Salesforce.

<apex:page>
    <html>

<head>
    <script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.6.42/dist/dbr.js"></script>
    <style>
        #videoview {
            position: relative;
            width: 90%;
            height: 64vh;
        }

        #videoContainer {
            position: relative;
            width: 100%;
            height: 100%;
            z-index: 1
        }
    </style>
</head>

<body>
    <h1>Dynamsoft JavaScript Barcode SDK</h1>
    <p></p>
    <div class="select">
        <a>Video source: </a>
        <select id="videoSource"></select>
    </div>
    <div>
        <button id="bt_start">Start</button>
        <button id="bt_stop">Stop</button>
    </div>
    <div id="performance">N/A</div>
    <div id="videoview">
        <div class="dce-video-container" id="videoContainer"></div>
    </div>
    <script>
        Dynamsoft.DBR.BarcodeReader.license = "LICENSE-KEY";
        var resWidth = 1280, resHeight = 720;
        var performanceReport = document.getElementById('performance');
        var btStart = document.getElementById("bt_start");
        var btStop = document.getElementById("bt_stop");
        var decoding_start = 0;
        var count = 0;
        var averageTime = 0;
        var total = 0;
        var videoSelect = document.getElementById('videoSource');
        var scanner = null;
        var cameras = null;
        var camerainfo = {};

        (async () => {
            scanner = await Dynamsoft.DBR.BarcodeScanner.createInstance();
            scanner.setUIElement(document.getElementById('videoContainer'));
            scanner._canvasMaxWH = 640;

            // get camera source
            cameras = await scanner.getAllCameras();
            for (var i = 0; i < cameras.length; i++) {
                let option = document.createElement('option');
                option.text = cameras[i].label || 'camera ' + i;
                camerainfo[option.text] = i;
                videoSelect.appendChild(option);
            }

            scanner.onFrameRead = results => {
                let decoding_end = Date.now();
                if (decoding_start == 0) {
                    decoding_start = decoding_end;
                    count = 0;
                    total = 0;
                    averageTime = 0;
                    return;
                }
                let time_cost = decoding_end - decoding_start;
                if (count == 60) {
                    count = 0;
                    total = 0;
                    averageTime = 0;
                };
                count += 1;
                total += time_cost;
                averageTime = total / count;

                decoding_start = decoding_end;

                // performanceReport.innerHTML = "";
                if (results.length > 0) {
                    let txts = [];
                    try {
                        let localization;
                        for (var i = 0; i < results.length; ++i) {
                            performanceReport.innerHTML = "Type: " + results[i].barcodeFormatString + ", Value: " + results[i].barcodeText + ", average time cost: " + averageTime + " ms <br>";
                            txts.push(results[i].barcodeText);
                            console.log("Type: " + results[i].barcodeFormatString + ", Value: " + results[i].barcodeText + ", average time cost: " + averageTime + " ms");
                        }
                        let out = txts.join(', ');
                    } catch (e) {
                    }
                }
                else {
                    // console.log("No barcode found");
                }
            };
            scanner.onUnduplicatedRead = (txt, result) => {
                // console.log(txt, result);
            };

            await scanner.open();
        })();

        btStart.onclick = function () {
            decoding_start = 0;
            (async () => {
                await scanner.open();
                scanner.setResolution(resWidth, resHeight);
            })();
        }

        btStop.onclick = function () {
            (async () => {
                await scanner.stop();
            })();
        }

        videoSelect.onchange = function () {
            decoding_start = 0;
            (async () => {
                if (scanner.isOpen()) {
                    await scanner.setCurrentCamera(cameras[camerainfo[videoSelect.value]]);
                    scanner.setResolution(resWidth, resHeight);
                }
            })();
        }
    </script>

</body>

</html>
</apex:page>

Decode barcode from camera video stream within Salesforce window

Adding Custom Tabs

With the Visualforce pages ready, create corresponding custom tabs in Salesforce to use them within the Salesforce window.

Salesforce custom tab

new visualforce tab

Creating the App

Now, group your custom tabs into an app that provides the required functionality as a unit.

Salesforce app

Salesforce app tabs

Packaging for AppExchange

Finally, create a package containing your app to share the barcode decoding functionality publicly on AppExchange.

Salesforce AppExchange package

Salesforce package app

Publishing Your App to Salesforce AppExchange

Visit the Salesforce partner page to view your package. After completing the business plan and passing the security review, you can submit your package to Salesforce AppExchange.

Salesforce package publishing

Source Code

https://github.com/yushulx/javascript-barcode-qr-code-scanner/tree/main/examples/9.x/salesforce_visualforce_page