Build and deploy iOS apps on Omarchy Linux (Apple Silicon and x86_64)
SwiftUI apps built on Omarchy Linux, installed on a physical iPhone over USB, with no Xcode and no macOS in the loop.
Current working set, verified 2026-10-03 on x86_64 Arch with the install script as a fresh user (FINDINGS.md item 22):
| Tool | Version | Source |
|------|---------|--------|
| Swift | 6.4.0 | AUR swift-bin |
| xtool | 1.20.1 | xtool-org/xtool AppImage |
| pymobiledevice3 | latest from PyPI at install time | venv |
| LLDB | 21.0.0 (Swift toolchain) | bundled with swift-bin |
| iOS SDK | iPhoneOS 27.0 | Xcode 27.0 |
The device install and LLDB loop were proven on 2026-09-09 on an M1 with Swift 6.3.3, xtool 1.19.0 and the iOS 26.5 SDK; they are not yet re-run on the 6.4 set. Confirmed on x86_64 (community report, Jon Kinney, 2026-09-15): the same flow works on a Framework Desktop with an iPhone 16, used for a real client project.
Works with a free Apple ID. Paid membership not required for device installs.
What you need
- An Apple Silicon or x86_64 Linux box running Omarchy (Arch-based). Both
swift-bin ships aarch64 and x86_64, and
xtool publishes an AppImage for each.
- An iOS device and a USB cable.
- An Apple ID (free) for one download from Apple:
Xcode.xipfrom
Version matching matters: the SDK pieces must come from an Xcode whose Swift
matches the installed swift-bin: Xcode 27 for swift-bin 6.4 (the
current AUR version), Xcode 26 for 6.3 (FINDINGS.md items 16 and 22). The
install script prints the matching Xcode when it stops at the SDK step.
Install
git clone https://github.com/joshuaswarren/omarchy-apple-dev
cd omarchy-apple-dev
./install-toolchain.sh
The script installs the toolchain, puts the toolchain's own clang first on
PATH for the SDK install, and tells you exactly what is left if anything is.
Safe to re-run. When it stops at the SDK step, download the matching
Xcode .xip from https://developer.apple.com/download/all/?q=Xcode and re-run:
XCODE_XIP=/path/to/Xcode.xip ./install-toolchain.sh
Verify with swift sdk list (should print darwin).
Already have a Mac with a matching Xcode? You can stream just the ~3 GB of
SDK pieces xtool needs instead of the full .xip — see Route B in
install-toolchain.sh section 5. Optional; the .xip route above needs no Mac.
Toolchain swaps (mise/asdf/manual)
Note on mise: its two swift-backend URL bugs are fixed on mise main
(#13293,
#13297) but not in a tagged
release as of 2026-09-17. With a build past those, mise install swift
does work on Omarchy once you supply three curses sonames Arch names
differently (FINDINGS 21):
./install-toolchain.sh --curses-compat
export LD_LIBRARY_PATH=~/.local/lib/curses-narrow-compat
mise install [email protected]
--curses-compat aliases every narrow curses soname the host is missing to
its wide twin inside ~/.local/lib/curses-narrow-compat (nothing under
/usr/lib is touched) and prints the export line. Pass an extracted
toolchain directory to have it verify that every soname resolves:
./install-toolchain.sh --curses-compat /path/to/swift-6.3.3-RELEASE-ubi9-aarch64.
The variable has to be in the shell — mise does not apply mise.toml
[env] to its post-extract swift --version check.
This repo still installs AUR swift-bin, which resolves the same thing at
package level and needs no shim.
Swapping the Swift toolchain — mise use -g swift@, an asdf switch, or a
manual reinstall — moves Swift to a different absolute path. That does not
touch what you actually paid for: USB pairing records, your Apple ID auth,
and the SDK cache all live in user-global paths and survive by design.
What survives a swap:
- Pairing —
~/.pymobiledevice3/(+/var/lib/lockdownrecords). - Apple ID auth —
~/.local/share/xtool/. - SDK cache —
~/.cache/xtool/darwin-iPhoneOS, kept by the.xtoolsdk
./install-toolchain.sh --repair
--repair re-registers the cached SDK (no .xip, no network) and prints a
survive-status summary: SDK source used, pairing location, auth state. It
exits nonzero with instructions when the cache is missing and no XCODE_XIP
is given.
First app
xtool new HelloOmarchy
cd HelloOmarchy
xtool dev run
xtool dev build alone produces xtool/HelloOmarchy.app (arm64 Mach-O).
Device
Plug the iPhone in, tap Trust when prompted, then:
./device-run.sh
Try Omarchy on Windows: If this Omarchy installation runs in the virtual
machine distributed by tryomarchy.com, complete the
Windows VM iPhone USB setup first. It provides
working USB passthrough and a patched usbmuxd for reliable app installs.
Bare-metal Omarchy installations do not need that guide.
Wireless deploy is blocked on iOS 26 for Linux-only setups, tested
exhaustively (FINDINGS.md 17): iOS gives each host its own encrypted
RemotePairing tunnel and offers no way for a Linux host to claim one — a Mac
that once enabled "Connect via Network" holds a working wireless tunnel,
everyone else is refused. USB deploy works everywhere with no Apple-side
gate. When your phone DOES hold a tunnel with some host, device-run.sh
documents the pymobiledevice3 tunneld bridge for that case, and
device-run.sh --rsd can drive any tunnel endpoint you hold.
Ship (App Store / TestFlight)
From an xtool project directory:
~/omarchy-apple-dev/ship.sh
ship.sh builds a release .app, compiles the project's one .xcassets
catalog (Xcode's single-size 1024 AppIcon is expanded to the App Store
sizes), stamps the build-environment keys App Store processing reads
(DTXcode, DTSDKName, …, and a UTC CFBundleVersion), signs it with
rcodesign, packages xtool/, and validates the .ipa offline:
bundle layout, Info.plist keys and version formats, Mach-O arch and minimum
OS, icons in Assets.car, profile type and app id, entitlements, team id,
and every sealed hash. Any FAIL stops it. Without an App Store Connect key
it signs with a local TEST identity, so the output proves the pipeline and
Apple will reject that signature.
To upload, once:
- Create an App Store Connect API team key (role App Manager with access to
.p8 file
anywhere you like.
- Create the app record in App Store Connect (Apps > + > New App). The API
Then:
ASC_KEY_PATH=/path/to/AuthKey_XXXXXXXXXX.p8 ASC_ISSUER_ID=<issuer-uuid> ASC_KEY_ID=XXXXXXXXXX \
~/omarchy-apple-dev/ship.sh --upload
With the key set, ship.sh registers the bundle id, creates an Apple
Distribution certificate (the private key stays in
~/.config/omarchy-apple-dev/distribution/) and an App Store profile, then
uploads through the App Store Connect build-upload API and prints Apple's
processing result. The upload step is unproven (FINDINGS.md item 23).
Known gap: iPad apps fail validation on the iPad Pro 167 px icon, because
AssetKit 1.0.0 cannot store it next to the 152 px icon (FINDINGS.md item 23).
iPhone-only apps (UIDeviceFamily = [1] in the app's Info.plist) pass.
Real projects
install-toolchain.sh also installs Linux stand-ins for Apple's actool and
xcstringstool into the darwin SDK, so packages that declare .xcassets or
.xcstrings resources build. Large projects need more open files than a login
shell allows: run ulimit -n 65536 before xtool dev build.
Known limits (FINDINGS.md item 24): branch-pinned dependencies fail in xtool
1.20.1; SwiftData @Model has no Linux macro plugin yet; asset types AssetKit
lacks (alternate app icons, symbol sets, HEIC) are left out with a warning.
compat/icecubes/setup.sh reproduces the IceCubesApp run.
Scripts
install-toolchain.sh: everything up to and including the SDK install;
--repair re-registers the cached SDK into the current toolchain after a
toolchain swap (see Toolchain swaps above); --curses-compat [ROOT]
creates the curses sonames a vendor (mise/swift.org) toolchain needs on
Arch and optionally verifies ROOT resolves.
device-run.sh: pair, install, launch, LLDB attach;--networkand
--rsd modes for wireless deploys (unverified).
ship.sh: App Store.ipabuild, offline validation, and--upload.
tools/asc.py (stamp, identity, validate, upload) and
tools/darwin-tools (xcassets and the Linux actool, on AssetKit) and
tools/xcstringstool (String Catalogs).
Findings
FINDINGS.md records the twenty-four findings behind the working
run: what broke and how each was fixed (SDK install failures, a clang version
mismatch that breaks SwiftUI, the unstated prerequisites for debugging on
iOS 17+), the Swift/Xcode version matrix (items 15-16), why a toolchain
swap breaks SDK registration and how --repair restores it (item 19),
the mise/ncurses soname story (items 20-21), the move to xtool 1.20 +
Swift 6.4 + Xcode 27 (item 22), the App Store path (item 23), and the
IceCubesApp compatibility run (item 24).
Notes
clangon PATH must be the Swift toolchain's own clang, not the system
__builtin_bit_cast size errors when compiling SwiftUI. Both scripts in
this repo put it first on PATH themselves; in your own shell use
PATH="$(dirname "$(readlink -f "$(command -v swift)")"):$PATH".
- Building SwiftUI pulls in simd/arm_neon headers; first build takes about a
License
MIT