Skip to main content

Feature: Mobile Builds (iOS & Android)

Date: 2026-02-12 Status: M1+M2 IMPLEMENTED — M3 Planned Phase: 3d (Mobile), moved up from Phase 4


1. Context & Motivation

Morphee is currently a desktop app (macOS, Windows, Linux) built on Tauri v2, plus a web frontend. To reach users where they spend most of their time — especially families, teachers, and teams — iOS and Android builds are essential.

Tauri v2 shipped stable mobile support in October 2024. The existing codebase already has partial mobile readiness:

  • #[cfg_attr(mobile, tauri::mobile_entry_point)] in lib.rs
  • crate-type = ["lib", "cdylib", "staticlib"] in Cargo.toml (staticlib needed for iOS)
  • Frontend runtime detection (isTauri()) and dual-backend routing (memoryClient, fsClient)
  • Responsive CSS with mobile breakpoints

The challenge is that 2 of 4 Rust crates (fastembed, lancedb) do not compile for mobile targets, requiring a phased approach.


2. Options Investigated

Option A: Tauri v2 Mobile (Chosen)

Use Tauri v2's built-in iOS/Android support. Same codebase, same React frontend, Rust backend runs natively on device.

  • Pros: Single codebase for all 5 platforms. Aligns with offline-first vision. Rust code runs natively. Existing #[cfg_attr(mobile)] support. Consistent Integration/Interface architecture.
  • Cons: fastembed and lancedb don't compile for mobile — need conditional compilation then alternatives. Some Tauri plugins are desktop-only. iOS build toolchain has rough edges. App store review overhead.
  • Estimated effort: M (Phase M1) + L (Phase M2) + L (Phase M3)

Option B: Progressive Web App (PWA) — Rejected

Ship the existing React frontend as a PWA.

  • Pros: Zero build complexity. Works on all mobile browsers immediately.
  • Cons: No Rust backend (no local ML, no LanceDB, no git). No real push notifications on iOS. No offline capability. Contradicts the core vision.
  • Why rejected: Morphee's differentiator is local compute and offline operation. A PWA can't access any of that.

Option C: React Native — Rejected

Build a separate mobile app using React Native.

  • Pros: Mature mobile ecosystem. Native UX patterns.
  • Cons: Completely separate codebase. No Rust integration. Doubles maintenance. Diverges from Tauri/Rust architecture.
  • Why rejected: Maintaining two frontends with different tech stacks is not viable for our team size. The Rust backend is core to the product.

Option D: Capacitor — Rejected

Wrap the existing Vite/React app in Capacitor for iOS/Android.

  • Pros: Reuses existing React code. Simpler than Tauri mobile.
  • Cons: No Rust backend on mobile. Another framework alongside Tauri. No offline ML.
  • Why rejected: Same fundamental problem as PWA — no access to Rust. Adding a second hybrid framework creates fragmentation.

3. Decision

Chosen approach: Option A — Tauri v2 Mobile

Reasoning: It's the only option that preserves the Rust backend and offline-first vision. The codebase is already 80% ready (mobile entry point, staticlib, runtime detection). The crate compatibility issues are solvable with conditional compilation (Phase M1) and alternative crates (Phase M2).

Trade-offs accepted:

  • Phase M1 mobile will be online-only (Python backend required) — offline comes in Phase M2/M3
  • fastembed must be replaced by candle for mobile embeddings (pure Rust, no C++ dependency)
  • lancedb must be replaced by SQLite + vector extension on mobile (until lance fixes Android/iOS targets)
  • Mobile UX will start as "desktop layout on mobile" and improve iteratively

4. Technical Analysis

Rust Crate Compatibility

CrateiOSAndroidIssueMobile Alternative
fastembed v5 (ONNX)No prebuilt binariesNo prebuilt binariesort only ships desktop ONNX Runtime binariescandle (pure Rust, GGUF/ONNX, no C++ dep)
lancedb v0.26Untested, SIMD failsFailslance#2411SIMD CPU detection only handles macOS/LinuxSQLite + sqlite-vec or in-memory HNSW
git2 v0.20 (vendored)WorksWorks (NDK toolchain)C library cross-compiles via cc crateN/A — use as-is
keyring v3Works (apple-native)Needs alternativekeyring doesn't support Androidandroid-keyring crate or Kotlin Tauri plugin

Tauri Plugin Compatibility

PluginDesktopMobileImpact on Morphee
Shell (currently used)YesAndroid only (limited)Low — only used for git subprocess (we use git2 instead)
NotificationsYesYesGood — push notifications work
BiometricNoYesOpportunity — biometric unlock on mobile
Dialog, Clipboard, FSYesYesGood — all work
HTTP ClientYesNoNone — we use browser fetch()
WebSocket pluginYesNoNone — we use browser WebSocket

Conditional Compilation Strategy

// In Cargo.toml — desktop-only dependencies
[target.'cfg(not(any(target_os = "ios", target_os = "android")))'.dependencies]
fastembed = "5"
lancedb = "0.26"
arrow-array = "57"
arrow-schema = "57"

// In Cargo.toml — all platforms
[dependencies]
git2 = { version = "0.20", features = ["vendored-libgit2"] }

// In Cargo.toml — platform-specific keyring
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
keyring = { version = "3", features = ["apple-native"] }

[target.'cfg(target_os = "windows")'.dependencies]
keyring = { version = "3", features = ["windows-native"] }

[target.'cfg(target_os = "linux")'.dependencies]
keyring = { version = "3", features = ["linux-native"] }
// In embeddings.rs — conditional module
#[cfg(not(any(target_os = "ios", target_os = "android")))]
mod desktop_embeddings { /* fastembed implementation */ }

#[cfg(any(target_os = "ios", target_os = "android"))]
mod mobile_embeddings { /* stub or candle implementation */ }

Mobile-Specific Tauri Configuration

Tauri v2 supports per-platform config. The main tauri.conf.json stays as-is for desktop. Mobile gets its own project files:

  • iOS: src-tauri/gen/apple/ (Xcode project, generated by tauri ios init)
  • Android: src-tauri/gen/android/ (Gradle project, generated by tauri android init)

Key mobile config differences:

  • No window chrome, no min width/height, no resizable
  • Bundle identifier: app.morphee.mobile (iOS) / app.morphee.android (Android)
  • Deep link schemes for SSO callback: morphee://auth/callback
  • iOS: PrivacyInfo.xcprivacy for App Store requirements

CI/CD: GitHub Actions for Mobile

iOS Build Job:

  • Runner: macos-latest (already used for desktop macOS)
  • Requires: Xcode (pre-installed), CocoaPods
  • Apple signing: certificates already in GitHub Secrets
  • Rust targets: aarch64-apple-ios, aarch64-apple-ios-sim, x86_64-apple-ios
  • Output: .ipa file uploaded to GitHub Release + App Store Connect (via xcrun altool or Fastlane)

Android Build Job:

  • Runner: ubuntu-latest or macos-latest
  • Requires: Android SDK, NDK, JDK
  • Rust targets: aarch64-linux-android, armv7-linux-androideabi, i686-linux-android, x86_64-linux-android
  • Signing: Android keystore in GitHub Secrets
  • Output: .apk / .aab uploaded to GitHub Release + Google Play Console

5. Implementation Plan

StepDescriptionEffortDependencies
M1.1Tauri mobile project scaffolding (tauri ios init, tauri android init)SNone
M1.2Conditional compilation — gate fastembed + lancedb behind cfg(not(mobile))SM1.1
M1.3Mobile Rust stubs — embedding/vector commands return "not available on mobile"SM1.2
M1.4Frontend graceful degradation — isMobile() detection, skip local ML featuresSM1.2
M1.5Mobile-specific tauri.conf.json adjustments (no window chrome, deep links)SM1.1
M1.6Touch/responsive UI polish (navigation, chat input, gestures)MM1.1
M1.7CI/CD — GitHub Actions for iOS build + signingMM1.1
M1.8CI/CD — GitHub Actions for Android build + signingMM1.1
M1.9Test on physical devices + simulators, fix platform-specific issuesMM1.1–M1.8
M1.10App Store + Google Play initial submissionSM1.9
M2.1Replace fastembed with candle-based embeddings on mobile (pure Rust)LM1 complete
M2.2Replace lancedb with SQLite + vector extension on mobileLM1 complete
M2.3Android keystore integration for keyringMM1 complete
M2.4Push notifications (Tauri notifications plugin)SM1 complete
M2.5Deep linking for SSO callbacks on mobileSM1 complete
M3.1Local LLM on mobile via candle (small quantized models)LM2.1
M3.2Full offline memory system on mobileLM2.1, M2.2
M3.3Background sync + queued actionsMM3.2
M3.4Auth token caching for offline → online transitionSM3.2

Phase M1 total: Medium (sum of many S items + a few M items) Phase M2 total: Large Phase M3 total: Extra Large


6. Questions & Answers

QuestionAnswer
Target both platforms simultaneously?Yes — same priority for iOS and Android
Where in the roadmap?Moved up to Phase 3d (after SSO, before Channels)
Apple Developer + Google Play accounts?Both available. Apple certs already in GitHub Secrets
OK with online-only mobile initially?Yes for Phase M1, but offline is high priority (M2/M3)
Why not just compile the same crates for mobile?fastembed depends on ONNX Runtime C++ prebuilt binaries (desktop-only). lancedb has a known Android build failure in SIMD detection (lance#2411). Pure-Rust alternatives (candle, SQLite+vec) solve this.
Will candle work on mobile?Yes — it's pure Rust (no C++ build chain). It supports Metal on iOS and can use CPU on Android. Same crate planned for local LLM in Phase 3.
Impact on desktop?None. Desktop continues using fastembed + lancedb. Conditional compilation keeps the two paths separate.

7. Open Items

  • Verify git2 vendored cross-compilation for Android NDK — confirmed working (cc crate handles cross-compilation)
  • Decide on Android keystore approach — SQLite vault chosen (M2 implemented, replaces keyring stub)
  • Evaluate sqlite-vec vs in-memory HNSW for mobile vector search — rusqlite brute-force cosine chosen (M2 implemented, sufficient for mobile-scale data)
  • Define minimum device requirements (iOS version, Android API level)
  • Plan TestFlight (iOS) and Internal Testing (Android) beta distribution
  • Research Tauri's mobile background execution model (iOS app lifecycle, Android services)
  • Evaluate mobile binary size impact (Rust + WebView)
  • Plan model size constraints for on-device ML (mobile storage/memory limits)

8. Risks & Mitigations

RiskImpactMitigation
iOS Xcode integration fragilityBuild failures in CIPin Xcode version, test locally before CI changes
git2 fails to cross-compileNo local git on mobileFall back to Python backend git endpoints
Mobile binary size too largeApp store rejection (>200MB)Strip debug symbols, optimize Rust release profile, defer ML models to on-demand download
WebView performance on low-end AndroidSluggish UIPerformance testing early, optimize React rendering, consider lighter CSS
App Store review rejectionDelayed launchFollow Apple/Google guidelines from the start, prepare privacy policy + data declarations
candle on mobile has undiscovered issuesPhase M2 blockedTest candle cross-compilation early (in M1 as a spike)
ONNX Runtime builds from source for mobileFallback if candle doesn't cover all modelsVery complex, last resort — avoid this path

9. References