I spent a few hours trying out UTM SE (which, if you’re new here, is a just-released version of the UTM front-end for QEMU that runs on iOS) on my M1 iPad Pro, and quickly came to the conclusion that it is not really usable to do local development out of the box.
It might be great to, say, run Windows 95 or older DOS games (and I’m still sore that the Mac OS 9.2.1 image vanished), but unlike the “real” UTM, using UTM SE on iOS or an iPad is severely hobbled by the lack of a JIT.
However, given my recent rant on how I miss better ways to do small computing, I had to push the envelope a bit and try to figure out exactly how usable it might be in a pinch, so I plugged my iPad into my LG UltraFine and tried out some stuff so that you don’t have to–and I’m throwing in a few comparisons to a-Shell and iSH as well.
The Experiment
So far, being able to run arm64
binaries locally (even if apparently much slower than doing the equivalent in iSH) is UTM SE’s key differentiator for me.
Running sudo systemctl set-default multi-user.target
to disable the GUI and restricting the core count to 2 on the pre-built Debian XFCE image makes it reasonably usable, and highlights a couple of nice advantages:
- You get full Linux console emulation (and frame buffer graphics, should you need them). It can be quite slow, though.
- I can (theoretically) install and build just about everything I need–Go, Rust, Scheme, etc. It all depends on how long I want to wait.
And it does work–here’s me building an actual Rust binary on it:
![a bit rusty a bit rusty](/media/blog/2024/07/14/1430/stdbrPY9RVqdVm0PiHS7g2PxWVk=/utm.jpg)
The main drawbacks with UTM SE right now are all related to performance and integration:
- Copy/paste currently doesn’t work at all–not even via keyboard emulation.
- I can’t use the VM controls at all when UTM SE is on an external display, and if I move it to the iPad display, it will blank out the external display because it assumes I might want to have it as a secondary display adapter.
- Unlike a-Shell or iSH, the boot time takes a significant toll on usability–even in CLI mode, it takes almost a minute to be able to log in.
- The almost bare CLI system, with
htop
running in the foreground, takes up 10% of my iPad’s CPU. Doing anything of consequence (like editing files or compiling a small binary) will shoot at least past 30% when emulating just 2 cores. - Installing anything takes a long time because merely decompressing the packages is very slow–this is just one example of where a JIT would make a stupendous amount of difference. My initial experience with doing an
apt upgrade
consumed over a tenth of my iPad’s battery solely because of that. - And, of course, filesystem integration is not as smooth (I’ve actually yet to get it to work, but I believe it’s there).
Alternatives
The good thing is that there are alternatives if you want a CLI on an iPad–and they’re actually surprisingly useful, even if not as popular or well-understood as they probably ought to be.
a-Shell
a-Shell has been my go-to for almost any kind of scripting (Python, Lua, etc.) on the iPad since it came out–in fact, I’m posting this from vim
running inside it, and there’s very little actually lacking for a lot of simple programming tasks.
Since it is a mix of Nicholas Holzschuch’s excellent ios_system
and WebAssembly binaries, you have a variety of packages (including the sqlite
binary I contributed with Nicholas’ help), and it handles multi-tasking and multiple windows quite well (within the constraints of Stage Manager, of course), with minimal (almost residual) CPU usage.
Everything is very speedy, responsive and eminently useful (even if a bit quirky if you’re used to standard UNIX shells), and in all my time using it I’ve only experienced three recurring nuisances:
- The terminal emulation is a bit temperamental (and you get an extra always-on caret for selection, which is distracting and fiddly). That shows up when using
ssh
sometimes, but also with anything that expects full VT100 emulation. - There’s no native
git
(it ships withlg2
, which mostly works, but I have to resort to Working Copy to manage most of my code). - Due to the restricted environment, building your own binaries is a chore (it has to be done on a desktop machine), and you can’t really get most Python or Lua dependencies to build locally.
I’ve been able to do a lot inside it, and it is the closest to an iPad-native shell there is right now. Blink has recently begun incorporating some local binaries based on ios_system
and has superior terminal emulation, but despite my liking its vim
experience better due to that, it doesn’t really count as a local shell.
iSH
iSH has been around for a long time now (as such things go), and provides a restricted x86
(not 64-bit, just 32-bit) emulation environment with an Alpine Linux userland.
I’ve been using it to run fairly complex Python stuff that I just can’t get to work under a-Shell locally for almost a year now (like weasyprint
and other things I need for generating PDFs on the go), and it can compile most C binaries just fine, but not Go (binaries crash) or Rust (cargo
panics while building), plus gdb
plain doesn’t work on most occasions.
That said, even if it is still slow for an emulator (and will also impact your battery life), it is quite usable for running a lot of things, especially if they’re already available in Alpine–and it can access the iOS filesystem, too, making it a great complement for a-Shell.
Using a Sidecar
As much as it pains me to keep re-stating this, getting a small Linux single-board computer and using it as a sidecar is still the best option for developing on the go on an iPad.
At least when compared to UTM SE, not only will it be much faster and useful overall, the impact of not having a JIT is so great that your battery will last longer even if you power a modern quad-core SBC directly from the iPad.
So thank you Apple, yet again, for not letting us have nice things even if they would be completely sensible and greatly improve some of your most devoted users’ feelings towards your approach to, er… “supporting” developers.