DEV Community

druchan
druchan

Posted on • Originally published at code.druchan.com on

Building mobile apps using Elm and Capacitor

Native mobile app dev is still largely object-oriented driven (Kotlin, Java etc). Languages like Kotlin now have a lot of functional programming paradigms supported out of the box (e.g. Arrow).

About two years ago, I wanted to see if Elm can be used to build a meaningful Android app.

I manage my expenses on a simple Google spreadsheet. When I'm on the move, I'd jot down the expenses as notes and then type them out on the spreadsheet once I had access to my laptop. I wanted a way to add expenses directly to my spreadsheet through my phone. The Google Sheets mobile app does not offer a great UX for this.

I ended up building a (highly-personalized) expense tracking Android app using Elm. I've been using this app ever since and has hardly needed a few updates in all this time.

Recently, I updated the "bootstrap" repo that I use to build Elm-based Android apps. (These can be used to build iOS apps as well). You can grab the source-code / clone the repo from here.

But if you're interested in setting up an Elm-based Android app project yourself, here's my notes:

  1. Core dependencies
  2. Bundling logic – how is the project built?

1. Core dependencies

For this project, I use CapacitorJS. This means the whole application runs inside a web-view.

Things have improved quite a bit in the web-view (and mobile browser engine) space in the recent years so building apps that run on web-views is not really a bad thing now.

For styling, I use TailwindCSS. It's simple, clean and has one of the best styling ecosystems that one can ask for.

Custom JS glue-code has to be written to make your Elm app interact with native things (via CapacitorJS) and you'd ideally write these in ES6 or later. So, the app would need a way to be "transpiled" and/or bundled. To do this, I use Parcel. Not exactly the best solution out there but it's far more than enough for a bootstrap. Eventually, I'd like to swap this out with Vite to see how things work.

2. Bundling logic – How is the project built?

If we were building an Elm app, this is broadly the workflow:

  • write Elm code
  • compile Elm to JS
  • include the compiled JS in an index.html file and init the Elm app via Elm.Main.init()
  • serve the html file and JS assets.

Building a mobile app is almost the same, except for a couple of steps:

  • include Capacitor-JS related code (in Javascript) – typically, Elm and Capacitor would communicate via ports,
  • and because Capacitor-related code will end up being ES6 or later, use a bundler like Parcel

The bootstrap's project structure is simple:

public
├── css
│   ├── index.css
│   └── style.css
├── index.html
└── js
    ├── elm.js
    └── index.js

Enter fullscreen mode Exit fullscreen mode

Here's more info:

public
├── css
│   ├── index.css <-- all your custom CSS goes here
│   └── style.css <-- this file gets auto-generated by the `yarn build` command
├── index.html <-- main entry-point for the project. Parcel will use this file to build/bundle the project.
└── js
    ├── elm.js <-- this file is auto-generated by Elm during the `yarn build` process
    └── index.js <-- all your custom Capacitor / other JS can go in this file (and other JS files)

Enter fullscreen mode Exit fullscreen mode

The build step does these things:

  • it compiles Elm code to JS
  • it builds a minified CSS file
  • then it lets Parcel bundle the whole project into a separate directory (web)
  • and finally, it lets Capacitor "sync" the project which is basically Capacitor copying over the web into the Android/iOS project folder.

3. Caveats and explorations

One of the things I realized while building the personal expense tracking app was that Elm routing doesn't work. So I had to resort to using Browser.element and using a custom Page type as part of the Model.

Elm ports can feel a little tedious to hook up with the Capacitor bridge. However, with some good abstractions, using ports can be more streamlined.

Capacitor is not the only JS-native bridge. There are also other tools like NativeScript and it could be worth exploring how that plays with an Elm project.

Sentry blog image

The Visual Studio App Center’s retiring

But sadly….you’re not. See how to make the switch to Sentry for all your crash reporting needs.

Read more

Top comments (0)

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay