Skip to main content
Technology & EngineeringTauri404 lines

Tauri Mobile

Tauri 2.0 mobile development for iOS and Android, including platform-specific code, mobile plugins, testing on simulators and devices, and app store distribution.

Quick Summary36 lines
You are an expert in building mobile applications with Tauri 2.0, covering iOS and Android builds, platform-specific code, mobile plugins, and app store distribution.

## Key Points

- **Designing desktop-first and porting later**: Responsive design from the start is far easier than retrofitting. Plan for touch and small screens from day one.
- **Using desktop-only plugins on mobile**: Not all plugins support mobile. Check plugin documentation for platform support.
- **Ignoring mobile lifecycle events**: Mobile apps get suspended, resumed, and terminated differently than desktop apps. Handle these events.
- **Testing only in simulators**: Simulators miss real-device issues like memory pressure, slow storage, and actual touch behavior.
1. Enroll in the Apple Developer Program ($99/year)
2. Create App ID in Apple Developer Portal
3. Configure signing certificates and provisioning profiles
4. Set up App Store Connect listing
5. Build with `cargo tauri ios build`
6. Upload via Xcode or Transporter
7. Submit for review
1. Enroll in Google Play Developer Program ($25 one-time)

## Quick Example

```bash
# Initialize iOS project
cargo tauri ios init

# Initialize Android project
cargo tauri android init
```

```bash
# Build for iOS (release)
cargo tauri ios build

# The .ipa file is generated in:
# src-tauri/gen/apple/build/arm64/MyApp.ipa
```
skilldb get tauri-skills/Tauri MobileFull skill: 404 lines
Paste into your CLAUDE.md or agent config

Tauri Mobile — iOS and Android Development

You are an expert in building mobile applications with Tauri 2.0, covering iOS and Android builds, platform-specific code, mobile plugins, and app store distribution.

Core Philosophy

Tauri 2.0 brings the same architecture to mobile: a Rust core process communicating with a webview frontend through typed IPC. On iOS, the webview is WKWebView; on Android, it is Android WebView. Your existing Tauri desktop code can share most of its Rust backend and all of its frontend code with mobile targets, with platform-specific adaptations where needed.

Mobile is not an afterthought bolted onto a desktop framework. Tauri 2.0 was redesigned specifically to support mobile as a first-class target. But mobile platforms have different constraints: smaller screens, touch input, background process restrictions, and app store review requirements. Design your UI responsively from the start, and use platform checks to adapt behavior where desktop and mobile diverge.

Do not expect pixel-perfect parity across platforms. The webview engines differ (WKWebView vs Android WebView vs WebView2), and mobile webviews may lag behind desktop versions in feature support. Test early and often on real devices.

Anti-Patterns

  • Designing desktop-first and porting later: Responsive design from the start is far easier than retrofitting. Plan for touch and small screens from day one.
  • Using desktop-only plugins on mobile: Not all plugins support mobile. Check plugin documentation for platform support.
  • Ignoring mobile lifecycle events: Mobile apps get suspended, resumed, and terminated differently than desktop apps. Handle these events.
  • Testing only in simulators: Simulators miss real-device issues like memory pressure, slow storage, and actual touch behavior.

Prerequisites

iOS Setup

# Install Xcode from the App Store, then:
xcode-select --install

# Install CocoaPods (if not using Swift Package Manager)
sudo gem install cocoapods

# Verify iOS targets for Rust
rustup target add aarch64-apple-ios
rustup target add aarch64-apple-ios-sim  # For Apple Silicon simulators
rustup target add x86_64-apple-ios       # For Intel simulators

Android Setup

# Install Android Studio and set environment variables
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
export ANDROID_HOME="$HOME/Library/Android/sdk"
export NDK_HOME="$ANDROID_HOME/ndk/$(ls $ANDROID_HOME/ndk/ | head -1)"
export PATH="$PATH:$ANDROID_HOME/platform-tools"

# Install Rust Android targets
rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android

Initializing Mobile Targets

# Initialize iOS project
cargo tauri ios init

# Initialize Android project
cargo tauri android init

This generates platform-specific project files:

src-tauri/
├── gen/
│   ├── apple/              # Xcode project
│   │   ├── MyApp.xcodeproj
│   │   ├── MyApp_iOS/
│   │   │   ├── Info.plist
│   │   │   └── Assets.xcassets
│   │   └── Podfile
│   └── android/            # Android project
│       ├── app/
│       │   ├── build.gradle.kts
│       │   └── src/main/
│       │       ├── AndroidManifest.xml
│       │       └── java/
│       ├── build.gradle.kts
│       └── settings.gradle.kts

Development Workflow

Running on iOS Simulator

# List available simulators
cargo tauri ios dev --list

# Run on a specific simulator
cargo tauri ios dev --device "iPhone 15 Pro"

# Run on a physical device (requires signing)
cargo tauri ios dev --device "Your iPhone Name"

Running on Android Emulator/Device

# List available devices
cargo tauri android dev --list

# Run on emulator
cargo tauri android dev --emulator

# Run on connected device
cargo tauri android dev --device

Mobile Entry Point

// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_opener::init())
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

// src-tauri/src/main.rs (desktop only)
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
    app_lib::run();
}

Platform-Specific Code

Rust Side

#[tauri::command]
fn get_platform_info() -> String {
    if cfg!(target_os = "ios") {
        "Running on iOS".to_string()
    } else if cfg!(target_os = "android") {
        "Running on Android".to_string()
    } else if cfg!(target_os = "macos") {
        "Running on macOS".to_string()
    } else {
        "Running on desktop".to_string()
    }
}

// Platform-specific module
#[cfg(target_os = "ios")]
mod ios {
    pub fn configure_keyboard() {
        // iOS-specific keyboard handling
    }
}

#[cfg(target_os = "android")]
mod android {
    pub fn configure_back_button() {
        // Android back button handling
    }
}

Frontend Side

import { platform } from "@tauri-apps/plugin-os";

const currentPlatform = platform(); // "ios", "android", "macos", "windows", "linux"

function isMobile(): boolean {
  const p = platform();
  return p === "ios" || p === "android";
}

// Conditional UI rendering
function App() {
  if (isMobile()) {
    return <MobileLayout />;
  }
  return <DesktopLayout />;
}

Responsive CSS

/* Base styles (mobile-first) */
.container {
  padding: 16px;
  max-width: 100%;
}

.sidebar {
  display: none;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container {
    padding: 24px;
    max-width: 768px;
    margin: 0 auto;
  }
}

/* Desktop */
@media (min-width: 1024px) {
  .sidebar {
    display: block;
    width: 250px;
  }

  .container {
    max-width: 1200px;
  }
}

/* Safe area insets for iOS notch */
.header {
  padding-top: env(safe-area-inset-top);
}

.footer {
  padding-bottom: env(safe-area-inset-bottom);
}

Mobile Plugins

Haptic Feedback

// Using platform-specific APIs in Rust
#[cfg(target_os = "ios")]
#[tauri::command]
fn haptic_feedback() {
    // Call into iOS UIKit via objc crate
}

#[cfg(target_os = "android")]
#[tauri::command]
fn haptic_feedback(app: AppHandle) {
    // Call into Android Vibrator service
}

#[cfg(not(any(target_os = "ios", target_os = "android")))]
#[tauri::command]
fn haptic_feedback() {
    // No-op on desktop
}

Biometric Authentication

// Using a biometric plugin (community)
import { authenticate } from "@tauri-apps/plugin-biometric";

async function unlockApp() {
  try {
    await authenticate("Unlock to continue", {
      allowDeviceCredential: true,
    });
    // Authentication successful
  } catch (e) {
    console.error("Biometric auth failed:", e);
  }
}

Camera and Photos

// Platform-aware media handling
async function capturePhoto() {
  if (isMobile()) {
    // Use mobile camera plugin
    const photo = await invoke<string>("capture_photo");
    return photo;
  } else {
    // Use file picker on desktop
    const { open } = await import("@tauri-apps/plugin-dialog");
    return open({
      filters: [{ name: "Images", extensions: ["png", "jpg"] }],
    });
  }
}

Mobile Configuration

iOS Info.plist

<!-- src-tauri/gen/apple/MyApp_iOS/Info.plist -->
<dict>
    <key>NSCameraUsageDescription</key>
    <string>This app needs camera access to scan documents</string>

    <key>NSPhotoLibraryUsageDescription</key>
    <string>This app needs photo library access to import images</string>

    <key>UIStatusBarStyle</key>
    <string>UIStatusBarStyleLightContent</string>

    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
</dict>

Android Manifest

<!-- src-tauri/gen/android/app/src/main/AndroidManifest.xml -->
<manifest>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:usesCleartextTraffic="false"
        android:networkSecurityConfig="@xml/network_security_config">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="unspecified"
            android:windowSoftInputMode="adjustResize">
        </activity>
    </application>
</manifest>

Building for Distribution

iOS Build

# Build for iOS (release)
cargo tauri ios build

# The .ipa file is generated in:
# src-tauri/gen/apple/build/arm64/MyApp.ipa

Android Build

# Build APK (for sideloading/testing)
cargo tauri android build --apk

# Build AAB (for Play Store)
cargo tauri android build --aab

# Sign the AAB for Play Store
jarsigner -verbose -sigalg SHA256withRSA \
  -digestalg SHA-256 \
  -keystore my-release-key.jks \
  app-release.aab my-key-alias

App Store Submission Checklist

iOS (App Store)

  1. Enroll in the Apple Developer Program ($99/year)
  2. Create App ID in Apple Developer Portal
  3. Configure signing certificates and provisioning profiles
  4. Set up App Store Connect listing
  5. Build with cargo tauri ios build
  6. Upload via Xcode or Transporter
  7. Submit for review

Android (Play Store)

  1. Enroll in Google Play Developer Program ($25 one-time)
  2. Create app listing in Google Play Console
  3. Generate a signing key
  4. Build AAB with cargo tauri android build --aab
  5. Upload to Play Console
  6. Complete content rating questionnaire
  7. Submit for review

Testing on Devices

# iOS -- run on device with logging
cargo tauri ios dev --device "iPhone" -- --verbose

# Android -- run on device with logcat
cargo tauri android dev --device
adb logcat | grep -i tauri

# Remote debugging (Android)
# Open chrome://inspect in Chrome to debug the Android webview

# Remote debugging (iOS)
# Open Safari > Develop > [Device Name] > [Page]

Install this skill directly: skilldb add tauri-skills

Get CLI access →