Skip to main content
The two platform projects (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 .app and the debug .apk from a fresh clone.
Both projects already exist, so 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 SecretsPort is 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.
See Storage & Secrets for the native store’s layout, the atomic write path, and the one-time migration out of 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/android is generated locally and committed, so a Linux contributor never needs xcodegen or macOS to change Android code.
  • gen/apple is generated on the macos-15 runner the first time (the workflow uploads it as the gen-apple artifact) 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 no gen-apple artifact.
  • gen/apple/.gitignore and gen/android/.gitignore (and gen/android/app/.gitignore) are the template’s own — they exclude build/, 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_dispatch plus pull_request on src/praisonai-mobile/** and on the workflow file itself.
  • Concurrency group: mobile-platforms-${{ github.ref }} with cancel-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

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.
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.
The bundler exits 0 on a silently-skipped target — the workflow’s verification steps look inside the produced artifact on purpose.

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.