Raphael Ugwu Writer, software engineer, and a lifelong student.

Building cross-platform desktop apps with Svelte, Electron, and Reloadly

5 min read 1450

Introduction

When building apps, it’s great to have options of reusability across different platforms. Electron offers a way for web developers to broaden their skillsets while working on cross-platform desktop apps. Svelte is a framework that has recently proven to be applicable in different environments.

In this article, we will work with Svelte and Electron to build a mobile top-up recharge app. Previous experience with JavaScript and Svelte will be helpful in understanding this post.

Prerequisites

These are the tools you should have on your machine:

  • Node: A backend JavaScript runtime environment that runs on the V8 engine and executes JavaScript code outside a web browser
  • npm: A package manager for Node

Getting started with Svelte

To get started we will initiate a Svelte project, as this will serve as the interface where we will make most of our changes.

In your terminal, use the npx degit svelte/template command to create your new Svelte project:

// Add your preferred project name to the command:

npx degit sveltejs/template reloadly-app-svelte

// When complete, navigate to the newly created app:

cd reloadly-app-svelte

// Then install the necessary dependencies:

npm install 

At this stage, your Svelte project should have a folder structure similar to this:

Screenshot of folder structure in Svelte

Once you are done installing Svelte, the next step is to install Electron. Navigate to your terminal and in your newly created Svelte project, install Electron using npm:

npm install electron --save-dev

When done, create an index.js file in your Svelte project and in this file, indicate the desktop window that will be opened whenever your Electron app is launched:

// index.js

const { app, BrowserWindow } = require("electron");
const path = require("path");
app.on("ready", () => {
  const mainWindow = new BrowserWindow();
  mainWindow.loadFile(path.join(__dirname, "public/index.html"));
  mainWindow.webContents.openDevTools();
});

What’s left for your app to go live are a few configurations:

We made a custom demo for .
No really. Click here to check it out.

  • Making index.js the entry point for your app
  • Configuring Rollup (a module bundler installed when you created your Svelte project) to control you app’s build process
  • Setting Electron to launch your app

To achieve these, edit your package.json file to include the code sample below:

{
  ...
  "main": "index.js"
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "electron .",
  }
}

Before going live, ensure the following have been checked:

  • In index.js, the main window’s path is public/index.html
  • In public/index.html, the stylesheet and script links are global.css, build/bundle.css, and build/bundle.js

Once you have verified this, you can launch your app by running the npm run dev command on your terminal. Your app should launch with this interface:

Screenshot of Svelte app reading "hello world"

Getting and securing your access token

A key part of the mobile top-up recharge app you are building lies in making HTTP requests in Svelte and displaying their responses on your Electron app. For the API, Reloadly is a viable option as it provides endpoints for making top-ups to mobile numbers.

To make secure HTTP requests to Reloadly’s API, you will need client credentials and an access token. Credentials can be accessed from Reloadly’s dashboard, and then used to make a POST request for an access token. There are a lot of options to use when making this request but Reqbin is preferred for being minimalist:

Screenshot of reloadly request

What’s happening in the image above?

  • A POST request was made to this URL: https://auth.reloadly.com/oauth/token
  • The body of this request has the client credentials ( client-id and client-secret), grant-type, and audience as parameters
  • The response of this request contains the access token, its permissions, and how long it is to last before expiring (in seconds)

To secure your access token, you need to store it in an environment separate from your codebase. This can be achieved by saving it to a .env file, then installing dotenv to link this file to your codebase without exposing your access token.

First, create the .env file in your Svelte project and save your access token:

/ .env

ACCESS_TOKEN = Bearer eyJraWQiOiIwMDA1YzFmMC0xMjQ3LTRmNmUtYjU2ZC1jM2ZkZDVmMzhhOTIiLCJ0eXAiOiJKV1QiLCJ5jZSByZWFkLXByZXBhaWQtY29tbWlzc2lvbnMiLCJleHAiOjE2MjE3Njc1NzQsImh0dHBzOi8vcmVsb2FkbHkuY29tL2p0aSI6ImZmYjgyMGEyLTE1MmEtNGYhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2ODkzIiwiaXNzIjoiaHR0cHM6Ly9yZWxvYWRseS5hdXRoMC5jb20vIiwiaHR0cHM6Ly9yZWxvYWRseS5jb20vc2FuZGJveCI6ZmFsc2UsImh0dHBzOi8vcmVsb2FkbHkuY29tL3ByZXBhaWRVc2VIiwiYXVkIjoiaHR0cHM6Ly90b3B1cHMtaHMyNTYucmODkzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzGY4ZSIsImlhdCI6MTYxNjU4MzU3NCwianRpIjoiYjVhOTMyNGUtODFiOC00OGIyLWI5MWMtYzZVsb2FkbHkuY29tIiwibmJmIjoxNjE2NTgzNTc0LCJhenAiOiI2ODkzIiwic2NvcGUiOiJzZW5kLXRvcHVwcyByZWFkLW9wZXJhdG9ycyByZWFkLXByb21vdGlvbnMgcmVhZC10b3B1cHMtaGlzdG9yeSByZWFkLXByZXBhaWQtYmFsYW0ZC1hMmY4LWEwMTcwZDFiOySWQiOiI2kZWRiNDQ1YmRjIn0.z6_BQMwHbr_breyDm4YgRTiu3RdhJXKmu8fnlHrBHc4

Next, install dotenv alongside rollup/plugin-replace, a Rollup plugin that replaces targeted strings in files:

npm install --save-dev dotenv @rollup/plugin-replace

After this, update your rollupconfig.js file to import the installed packages and attach the .env config:

// rollup.config.js

import {config} from 'dotenv';
import replace from '@rollup/plugin-replace';

const production = !process.env.ROLLUP_WATCH;

export default {
    plugins: [
      replace({
        // stringify the object       
        __myapp: JSON.stringify({
          env: {
            isProd: production,
            ...config().parsed // attached the .env config
          }
        }),
      }),
    ],
  };

Once this is done, you can proceed to structure your API request to make top-ups.

Structuring Reloadly’s API request

Navigate to your App.svelte file to make a POST request using Reloadly’s API.

First, define all parameters required for the request:

// .src/App.svelte 

<script>
    const accessToken = __myapp.env.ACCESS_TOKEN  // fetches your access token
    let results;
    let operatorId = '341'
    let recipientPhone = {
        'countryCode' : 'NG',
        'number' : ''
    }
    let amount = ''
    let customIdentifier = ''

    let headers = {
        'Content-Type' : 'application/json',
        'Authorization' :  accessToken 
    }
</script>

To handle the responses you will get from the API to request and present the data, you will need to define a few functions in your Svelte app:

  • handleClick(): This wraps the POST request and sets its execution to a button
  • hold(customIdentifier): This ensures that the app does not make a request until all inputs have been filled

Then make the request to Reloadly:

// .src/App.svelte

<script>
...
    const doPost = async () => {
        const response = await fetch('https://topups.reloadly.com/topups', {
            method: 'POST',
            body: JSON.stringify({
                recipientPhone,
                amount,
                operatorId,
                customIdentifier
            }),
            headers: headers
        })
        if (response.status === 200) {
      return await response.json();
    } else {
      throw new Error(response.statusText);
    }   
  }
</script>

Once a successful response is received, use a combination of HTML and Svelte elements to represent the data.

First, create input tags and a button to handle requests:

// . src/App.svelte

<div class = 'request'>
    <h1>RECHARGE ON THE GO  &#127757;</h1> <!-- &#127757 is an emoji -->
    <p> Phone number:   <input type=number bind:value={recipientPhone.number} /> </p>
    <p> Amount (NGN): <input type=number bind:value={amount} /> </p>
    <p> Transaction Reference: <input bind:value={customIdentifier} /> </p>
    <button disabled='{hold(customIdentifier)}' on:click={handleClick}>
      RECHARGE
    </button>
</div>

Then create a Svelte logic/await block to handle responses:

// .src/App.svelte

<div class = 'response'>
    {#await results }
        <p>Loading...</p>
    {:then res}
        <p>{res ? `Transaction ID: ${res.operatorTransactionId}` : ''}</p>
        <p>{res ? `Custom Identifier: ${res.customIdentifier}` : ''}</p>
        <p>{res ? `Operator Name: ${res.operatorName}` : ''}</p>
        <p>{res ? `Trannsaction Date: ${res.transactionDate}` : ''}</p>  
    {:catch res}
        <p>Error message: {res ? `${res.message}` : ''}</p>
    {/await}
</div>

Styling your app is similar to default CSS. Here’s an example:

// .src/App.svelte

<style>
   .request {
    padding: 20px;
    background-color: #242D3D;
    color: #1EBAD5;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
    text-align: center;
  }
</style>

At this point, your app should be complete. Launch it by running npm run dev and make a top-up to a mobile number:

Distribution configs in Electron

Now you’re done building your desktop app. The final procedure is to distribute it. To achieve this, you need to install electron-builder, a tool that packages and builds an Electron app ready for distribution.

Navigate to your terminal and install this tool:

npm install electron-builder --save-dev

Next, in your app’s package.json file, add the distribution config to include electron-builder:

scripts: {
  ...
  "dist": "npm run build && electron-builder"
}

Then run npm run dist in your terminal. This builds your app and creates a distributable version of your app as well:

Screenshot of folders in Svelte

Conclusion

Not only is working with different frameworks a great way to improve your skills, it’s better knowing that different options exist and, when combined, can be used to create amazing applications. You can check out the source code of this desktop app on GitHub.

Raphael Ugwu Writer, software engineer, and a lifelong student.

Leave a Reply