gen/apple, gen/android) are committed to the repo, and the mobile platforms workflow turns the crate into a real installable app on every mobile PR.
Quick Start
1
Run both builds locally
Build the simulator Both projects already exist, so
.app and the debug .apk from a fresh clone.init is a no-op — the builds run straight through.2
Or let CI do it for you
The
mobile platforms workflow runs on every PR that touches src/praisonai-mobile/**. Open the run page and download the PraisonAI-ios-simulator-app and PraisonAI-android-debug-arm64-apk artifacts.3
Regenerate a project if you truly need to
The init step is guarded: if
src-tauri/gen/apple (or gen/android) exists, it is not regenerated. Delete the folder first if you truly need a fresh one.How It Works
A mobile PR triggers CI, both jobs build inside the committed projects, and the artifacts land on the run page for a contributor to install.Storage per build
storageFor(kind, bridge, view) (app/src/platform.ts) picks the backing store from the detected platform, so the native build gets durable disk and the web build gets localStorage:
Only storage moved to a native adapter. Two ports stay on the web adapter under Tauri, deliberately, for now —
platform.ts says so in a comment (“a deliberate, temporary state and not an oversight”):
- Secrets — a keychain
SecretsPortis its own piece of work;createWebSecrets()is used on Tauri today. See Storage & Secrets → SecretsPort. - HTTP — a Rust-side
HttpPort(so a request is not subject to the WebView) is its own piece of work;createWebHttp()is used today.
localStorage.
Why both projects are committed
Committing both projects means any contributor builds either platform from a fresh clone, on any OS, with no init step.gen/androidis generated locally and committed, so a Linux contributor never needsxcodegenor macOS to change Android code.gen/appleis generated on themacos-15runner the first time (the workflow uploads it as thegen-appleartifact) and then committed, because the maintainer’s laptop has only Command Line Tools, not Xcode. Once committed, the init step becomes a no-op — the workflow’s own final run proves that, with nogen-appleartifact.gen/apple/.gitignoreandgen/android/.gitignore(andgen/android/app/.gitignore) are the template’s own — they excludebuild/,xcuserdata/,Externals/, and the like, so only source is committed.
The mobile-platforms workflow — job by job
The workflow lives at.github/workflows/mobile-platforms.yml and runs two jobs.
- Triggers:
workflow_dispatchpluspull_requestonsrc/praisonai-mobile/**and on the workflow file itself. - Concurrency group:
mobile-platforms-${{ github.ref }}withcancel-in-progress: true.
iOS — ios-simulator
Runs on macos-15 with Node 22.18 and Rust targets aarch64-apple-ios,aarch64-apple-ios-sim.
brew install xcodegen runs up front so the CLI does not prompt for it mid-run. The build uses --target aarch64-sim --no-sign --debug. A dedicated step copies src-tauri/icons/ios/*.png into Assets.xcassets/AppIcon.appiconset/, because tauri ios init writes the CLI’s own placeholder AppIcon and never replaces it (tauri icon writes only to src-tauri/icons/ios/, with no iOS-project wiring).
Android — android-apk
Runs on ubuntu-22.04 with JDK 17 Temurin — Gradle 8.14.3’s Groovy front end rejects JDK 25 with “Unsupported class file major version 69”, and 17 is what AGP 8.11 documents.
android-actions/setup-android installs platform-tools platforms;android-36 build-tools;36.0.0. The NDK is r28c (28.2.13676358) via nttld/setup-ndk@v1 with add-to-path: false, exported as NDK_HOME. The Rust target is aarch64-linux-android, and the build passes --target aarch64 on purpose — every phone sold in the last several years is arm64-v8a, and the universal segment in the output path is Gradle’s name for “not split per ABI”, not a claim about content. Verification asserts the APK contains lib/arm64-v8a/libpraisonai_mobile_lib.so.
Icons
Every raster the app needs is generated from one source vector, never edited by hand.
The mark is generated art, and says so — there was no source art (
icons/icon.png was a 512 px placeholder) and stores want 1024. Any redesign replaces the SVG and re-runs render.sh + npx tauri icon icon/icon.json.
What CI still can’t do (needs maintainer credentials)
The per-PR builds are unsigned; a real store upload needs credentials that are the maintainer’s and live outside the repo.
Nothing else is blocked, and neither credential is needed for the builds this workflow runs on every PR.
Best Practices
Don't hand-run tauri ios init / tauri android init on a checked-out tree
Don't hand-run tauri ios init / tauri android init on a checked-out tree
Both projects exist; re-init would fight the guard in the workflow. If you truly need to regenerate one, delete the folder first — this is the CLI’s own contract.
Regenerate icons through render.sh and npx tauri icon, never by hand
Regenerate icons through render.sh and npx tauri icon, never by hand
The Android adaptive icons and the iOS
AppIcon.appiconset are wired to specific filenames the tools own. Edit icon/app-icon.svg, then re-run render.sh and npx tauri icon icon/icon.json.Do not lower the Android minSdk below 26 without also lowering the WebView floor
Do not lower the Android minSdk below 26 without also lowering the WebView floor
Trust the workflow, not the step passing
Trust the workflow, not the step passing
The bundler exits 0 on a silently-skipped target — the workflow’s verification steps look inside the produced artifact on purpose.
Related
Shipping to Stores
The CSP, WebView floor, and monotonic versioning story.
Native Shell
The Tauri events and the back-gesture arbitration.
Getting Started
Clone, run in the webview, and get a chat answering.
Architecture
Boot order and where the shell is injected.

