░█▀▀░█▀█░░░░█▀▄░█▀█░█▀█░█▀▀░░░░█▀▀░█▀█░█▀█░█░░
░▀▀█░█░█░░░░█░█░█▀█░█░█░█░█░░░░█░░░█░█░█░█░█░░
░▀▀▀░▀▀▀░▀░░▀▀░░▀░▀░▀░▀░▀▀▀░▀░░▀▀▀░▀▀▀░▀▀▀░▀▀▀
        

Minimalist mobile app development :: 2024-05-15


Note: Much of the post content will likely be moved to some kind of knowledge base (or bootstrapping tool?) in the next few days.

I decided to make a mobile app for a project, and was interested in the options out there for cross-platform development. In particular, I'm interested in minimizing the effort required to maintain it over time and maximizing the amount of code that can be reused across Android, iOS, web, and native solutions.

For some background, I was a core engineer working in Amazon and AWS's Builder Tools organization, and helped define and maintain the underlying build infrastructure for projects like Amazon's retail shopping app, its Kindle app, and its Twitch app. Mobile development has not been a career focus of mine, but my work in infrastructure and Amazon's software ecosystem has facilitated faster iteration for apps that you may use in your day-to-day life. I've even spoken at internal conferences in Amazon specifically to demonstrate how to build mobile applications (the Amazon way). In other words, I've got more than a passing acquaintance.

First, some rejects

My goals may not be your goals, so don't take these suggestions as gospel. I don't have unlimited time or an unlimited budget for engineering talent, I am limiting my investment to just my own spare time. I'm also not doing anything that needs fast-rendering pixel-perfect graphics (Maybe then I'd use LÖVE).

Flutter is an attractive option. I love the Dart programming language and it comes with a large community that's reaching maturity. It also comes with a large package ecosystem with ready-to-go integrations for fiddly things like accepting in-app payments or integrating with advertisers. Those are not clear goals of mine though.

Generating a simple "hello world" Flutter app instantly filled me with dread, though. Gradle files, Xcode projects, CMakeLists.txt, and multiple sub-projects for each target. This instantly put Flutter out of reach for me and my smaller-scale ambitions. Each of those build systems to me represent a tower of dependencies and moving parts to keep updated over time, and much of the platform-specific code appears non-trivial. I'm not opposed to the systems (at least, not for the right price), but it's going way over my budget for time and effort, especially over time.

React Native is another attractive option, but again, my tolerance for a large tower of dependencies is low. If possible, I only want to have a handful of reasons to require updates. Changes to the OS or the store are impossible to avoid, but changes to (at the time of writing) 536 dependencies are certainly possible to avoid.

Other options like Xamarin, Iconic, or Electron are either delivering too few platforms or too many dependencies for my goals. Perhaps in a few years I'll choose Tauri, but it's not ready for mobile yet.

Fyne, I'll do it already

I've settled on Fyne as a framework I can work with. I don't know what the possible lower bounds on minimalism can be in the cross-platform world, but it comes with an order of magnitude fewer dependencies than React Native. In going through a similar "hello world" app, it becomes clear that the amount of code reuse between platforms will be very high. There is no extra build boilerplate like Gradle/Xcode/CMakeLists.txt to maintain. (Again, my goals may not be your goals, some projects will require the level of detail and optimization that can only be achieved with complex build procedures.) As a bonus, the Go programming language has a fantastic track record of backwards compatibility with previous versions of Go.

Minimalist Android Setup

The rest of this article will be notes on my development setup. As a warning, I prefer to work very minimally and to stick with text interfaces (CLIs, etc) that lend to automation, and I tend to prefer avoiding GUI-based interactions for software development whenever possible. If you're more of a GUI person, I recommend following Fyne's Getting Started guide.

1. Dev environment

First I setup a fresh Ubuntu machine specifically for app development. You don't have to do this separation, but with all the fiddliness I've experienced with Android in particular, I want to constrain resources (like disk usage) and avoid interfering unexpectedly with my primary system. I primarily use Void Linux, but there's a little better support for Android specifically from Ubuntu.

Originally I was working with Ubuntu in a QEMU VM on a local network host, and was able to get as far as emulation and successfully uploading an APK to a physical android phone. The phone was connected to my laptop via USB, and my laptop connected to the VM over Wi-Fi from a different room while watching a toddler. It's 2024 and it feels like almost anything is possible. The lag on input was bothering me, though, so I changed to Ubuntu on a cheap mini PC running XRDP for a remote connection and it's a lot snappier. I'm not really breaking the bank, just a small machine with about 8GB RAM and lotsa storage.

2. Dev dependencies

Next I setup the system with Go, GCC, graphics headers, and Go libraries. The initiated are going to understand this means Cgo is in the mix somewhere, and we're not dealing with "pure" Go. Personally I understand this as necessary when developing cross-platform, although I imagine it will change in the next 5 to 10 years.

Nothing too fancy here for Fyne, I followed the steps from the getting started guide.

I also installed a text editor (helix), an IDE (VS Code), and the Go language server (gopls). I'll use GitHub for controlling revisions of my software over time, so some setup in GitHub and on the machine for git was also performed. How you'll edit code will come down to some personal choices and go beyond the scope of this post.

A Java Runtime Environment (JRE) will be required for many of the Android tooling, so I also installed the most recent LTS version of Java using OpenJDK 21's JRE. On Ubuntu, I installed the openjdk-21-jre package with apt. I'll only be using Java for android dev tools and not for compilation or at runtime, so I don't feel the need to get fancy with Temurin or Corretto or some other hardened distribution, or to worry about Java build tools like Gradle/Maven/Bld.

3. Android dependencies

Here's where I will diverge from the crowd a bit. I start by installing command line tools only. This download requires a license agreement, which is a recurring theme with all android tooling, so I did this through a browser to my ~/Downloads directory.

cd "$HOME/Downloads"
unzip commandlinetools-linux-*.zip
./cmdline-tools/bin/sdkmanager --sdk_root="$HOME/android" --install 'cmdline-tools;latest'

Next, I add create an ANDROID_HOME environment variable, and add some android binary directories to my shell's PATH. This will vary by shell, but will look something like this:

# Bash
echo 'export ANDROID_HOME="$HOME/android"' >>~/.bashrc
echo 'export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"' >>~/.bashrc

# Zsh
echo 'export ANDROID_HOME="$HOME/android"' >>~/.zshrc
echo 'export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"' >>~/.zshrc

# Fish
set -U ANDROID_HOME "$HOME/android"
set -U fish_user_paths "$ANDROID_HOME/cmdline-tools/latest/bin" "$ANDROID_HOME/platform-tools" $fish_user_paths

Now source the new settings or restart your shell session as necessary.

This gets far enough to use sdkmanager to start installing the other required Android dependencies, and accepting yet more licenses.

sdkmanager --install ndk-bundle
sdkmanager --install platform-tools

And, believe it or not that's enough to...

4. Get started building an Android app

fyne package -os android -appID my.group.MyApp -icon icon.png

That creates an APK, which is the minimal runnable application for Android. To actually run the APK on an android device, you can attach one via USB to your development machine. The device needs to allow developer options to upload directly from USB, and we can use adb (Android Debug Bridge) from Android's platform-tools package to upload the file.

adb attach
adb install my_app.apk

I'll update later with details on dev workflow and emulation, but if you are a seasoned programmer you may be able to get on from this point.

5. Packaging the Android app for distribution in the Play store

Coming soon

Minimalist iOS Setup

Coming soon