Setup library for ElixirDesktop — build native iOS and Android apps from your Phoenix application.
- Erlang/OTP 28 and Elixir 1.19 (managed via mise)
- macOS for iOS builds; macOS or Linux for Android builds
mise install erlang@28.3.1
mise install elixir@1.19.5-otp-28brew install carthage xcodegen- Xcode 16+ (iOS 17.4+ deployment target)
- Android Studio with SDK 36, NDK, and CMake 3.22.1
- Java 17
Add desktop_setup to your list of dependencies in mix.exs:
def deps do
[
{:desktop_setup, github: "thehaigo/desktop_setup", only: :dev}
]
endmix desktop.installThis modifies your Phoenix app's config, endpoint, application module, and mix.exs for ElixirDesktop compatibility. The task is idempotent — safe to run multiple times.
mix desktop.setup.ios
cd native/ios
open YourAppName.xcodeprojBuild and run from Xcode. If you encounter build errors, try running the build script manually:
cd native/ios
./run_mixmix desktop.setup.androidOpen native/android in Android Studio and build. If you encounter build errors:
cd native/android
./app/run_mixIf your app uses SQLite (ecto_sqlite3), convert Ecto migration files for on-device execution:
mix desktop.migrations.convertThis converts priv/repo/migrations/*.exs into compiled modules under lib/<app>/migrations/ that run at app startup.
Phoenix App --> mix release --> app.zip
|
Native Shell (Swift / Kotlin)
|
Erlang VM (liberlang)
|
TCP Bridge Protocol
|
Phoenix Endpoint --> WebView
Both iOS and Android use the same Bridge protocol: a TCP socket carrying length-prefixed JSON messages between the Erlang VM and the native shell. The native side manages app lifecycle (background/foreground), WebView display, and system integration (file upload, external browser, dialogs).
| Feature | iOS | Android |
|---|---|---|
| Native language | Swift (SwiftUI) | Kotlin |
| Bridge transport | NWListener (TCP) | ServerSocket (TCP) |
| WebView | WKWebView | android.webkit.WebView |
| File upload | Native WKWebView support (iOS 12+) | onShowFileChooser + ActivityResultLauncher |
| Background handling | scenePhase → suspend/reinit | onStop/onStart → suspend/reconnect |
| Erlang VM | liberlang.xcframework (static) | liberlang.so (shared, per ABI) |
| Build tool | XcodeGen + Carthage | Gradle + CMake |
If you use PostgreSQL, add the following to config/prod.exs:
config :your_app, YourApp.Repo,
username: "postgres",
password: "postgres",
database: "your_app_dev",
stacktrace: true,
show_sensitive_data_on_connection_error: true,
pool_size: 10
if Mix.target() == :ios do
config :your_app, YourApp.Repo, hostname: "127.0.0.1"
end
if Mix.target() == :android do
config :your_app, YourApp.Repo, hostname: "10.0.2.2"
endThere is no XCFramework found at 'ios/Carthage/Build/ZIPFoundation.xcframework'.
cd native/ios
carthage update --platform iOS --use-xcframeworksSandbox: bash(39046) deny(1) file-read-data
In Xcode: Build Settings → Build Options → User Script Sandboxing → No
/dev/kvm device: permission denied
sudo chown $USER /dev/kvm- Desktop app setup
- iOS app setup
- Android app setup
- Erlang/OTP 25 → 26 → 27 → 28
- JS dialog support (alert/confirm/prompt) on both platforms
- JS console log forwarding to native debug console
- File upload support on both platforms
- Background/foreground lifecycle handling with LiveView reconnection