src/skills/desktop-mobile-tauri/SKILL.md
Tauri 2.x mobile development - iOS via WKWebView, Android via Android WebView, mobile plugins, Swift/Kotlin native code, permissions, debugging
npx skillsauth add agents-inc/skills desktop-mobile-tauriInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Tauri 2.x supports iOS (WKWebView) and Android (Android WebView) from the same codebase as desktop. Initialize with
tauri android init/tauri ios init, run withtauri android dev/tauri ios dev. Mobile-only plugins (biometric, barcode-scanner, NFC, haptics, geolocation) use#[cfg(mobile)]for conditional registration. Custom native code uses Swift classes extendingPluginon iOS and Kotlin classes annotated with@TauriPluginon Android. Every mobile plugin needs platform permissions (Info.plist keys on iOS, AndroidManifest.xml permissions on Android) in addition to Tauri capability grants.Current version: Tauri 2.x (stable). Mobile support is production-ready since Tauri 2.0 (2024).
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use #[cfg(mobile)] when registering mobile-only plugins -- registering them unconditionally breaks desktop builds)
(You MUST add platform permissions (Info.plist on iOS, AndroidManifest.xml on Android) in ADDITION to Tauri capability file permissions -- missing platform permissions cause silent failures or runtime crashes)
(You MUST use #[cfg_attr(mobile, tauri::mobile_entry_point)] on pub fn run() -- without it, the app cannot launch on mobile)
(You MUST run mobile dev commands (tauri ios dev, tauri android dev) instead of tauri dev for mobile targets -- tauri dev only targets desktop)
</critical_requirements>
Auto-detection: tauri android init, tauri ios init, tauri android dev, tauri ios dev, tauri-plugin-biometric, tauri-plugin-barcode-scanner, tauri-plugin-nfc, tauri-plugin-haptics, tauri-plugin-geolocation, #[cfg(mobile)], #[cfg(target_os = "android")], #[cfg(target_os = "ios")], mobile_entry_point, Info.plist, Info.ios.plist, AndroidManifest.xml, NSCameraUsageDescription, NSFaceIDUsageDescription, NSLocationWhenInUseUsageDescription, @TauriPlugin, Plugin Swift class, WKWebView, Invoke, InvokeArg, run_mobile_plugin, develop-mobile
When to use:
When NOT to use:
Key patterns covered:
#[cfg(mobile)] (examples/core.md)Detailed resources:
Tauri mobile extends the same Rust backend + webview frontend architecture to iOS and Android. The key difference: mobile apps run in the OS native webview (WKWebView on iOS, Android WebView on Android) and can access device hardware through mobile-specific plugins. Your existing Tauri desktop code (commands, state, events) works on mobile without changes -- you add mobile support incrementally.
When Tauri mobile is the right choice:
When Tauri mobile may NOT be the right choice:
Initialize mobile targets in an existing Tauri project. Each platform requires its own init step.
# Initialize Android target (generates gen/android/ project)
npx tauri android init
# Initialize iOS target (generates gen/apple/ project) -- macOS only
npx tauri ios init
After init, add the mobile entry point attribute to your run() function:
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![/* commands */])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Key point: #[cfg_attr(mobile, tauri::mobile_entry_point)] is required for mobile builds. Without it, the app cannot start on iOS or Android. The attribute is a no-op on desktop, so it is safe to always include. See examples/core.md for prerequisites and environment setup.
Mobile-only plugins must be conditionally registered to avoid breaking desktop builds.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let mut builder = tauri::Builder::default();
// Mobile-only plugins -- conditional registration
#[cfg(mobile)]
{
builder = builder
.plugin(tauri_plugin_biometric::init())
.plugin(tauri_plugin_barcode_scanner::init())
.plugin(tauri_plugin_nfc::init())
.plugin(tauri_plugin_haptics::init())
.plugin(tauri_plugin_geolocation::init());
}
builder
.invoke_handler(tauri::generate_handler![/* commands */])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Key point: Using #[cfg(mobile)] ensures these plugins are only compiled and registered on iOS/Android. The Cargo dependencies should also be conditional. See examples/core.md for Cargo.toml configuration.
Use #[cfg(target_os)] for platform-specific logic in commands or setup.
#[tauri::command]
fn get_platform_info() -> String {
#[cfg(target_os = "android")]
{ "Running on Android".to_string() }
#[cfg(target_os = "ios")]
{ "Running on iOS".to_string() }
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{ "Running on desktop".to_string() }
}
Key point: #[cfg(mobile)] is shorthand for #[cfg(any(target_os = "android", target_os = "ios"))]. Use the specific target_os when behavior differs between Android and iOS. See examples/core.md for conditional dependency examples.
Mobile plugins require two layers of permissions: Tauri capability file grants AND native platform permission declarations.
<!-- src-tauri/Info.ios.plist (iOS) -->
<key>NSCameraUsageDescription</key>
<string>Required to scan barcodes</string>
<key>NSFaceIDUsageDescription</key>
<string>Authenticate to access secure features</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Required for location-based features</string>
<!-- gen/android/app/src/main/AndroidManifest.xml (Android) -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" android:required="true" />
Key point: Missing platform permissions cause silent failures or OS-level denials, even when Tauri capabilities are correctly configured. iOS needs usage description strings explaining WHY the app needs each permission. See examples/core.md for the full permission layering pattern.
Write native iOS code by extending the Tauri Plugin class in Swift.
import Tauri
import WebKit
class MyPlugin: Plugin {
@objc public func doSomething(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(DoSomethingArgs.self)
// Native iOS API calls here
invoke.resolve(["result": "success"])
}
}
class DoSomethingArgs: Decodable {
let input: String
var optional: Bool?
}
Key point: Methods must have @objc attribute and accept an Invoke parameter. Arguments are parsed via Decodable classes. Use invoke.resolve() to return data or invoke.reject() to return errors. See examples/native-plugins.md for complete examples.
Write native Android code with @TauriPlugin annotation and @Command methods.
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.Plugin
@InvokeArg
internal class DoSomethingArgs {
lateinit var input: String
var optional: Boolean = false
}
@TauriPlugin
class MyPlugin(private val activity: Activity) : Plugin(activity) {
@Command
fun doSomething(invoke: Invoke) {
val args = invoke.parseArgs(DoSomethingArgs::class.java)
val ret = JSObject()
ret.put("result", "success")
invoke.resolve(ret)
}
}
Key point: Commands annotated with @Command run on the main thread by default. Long-running operations must use coroutines or background threads to avoid freezing the UI. Arguments use @InvokeArg annotation with lateinit var for required fields. See examples/native-plugins.md for async patterns and Rust interop.
Run and debug on mobile devices/simulators.
# Run on iOS simulator (macOS only)
npx tauri ios dev
# Run on specific iOS device/simulator
npx tauri ios dev 'iPhone 16'
# Run on Android emulator
npx tauri android dev
# Open in Xcode / Android Studio for native debugging
npx tauri ios dev --open
npx tauri android dev --open
Debugging approaches:
chrome://inspect in Chrome > select connected devicetauri-plugin-log for structured logging across platformsadb logcat (Android)Key point: The --open flag launches the IDE but the Tauri CLI process must stay running. For physical devices, the dev server must be reachable on the local network -- the CLI handles this via TAURI_DEV_HOST. See examples/core.md for physical device setup.
<decision_framework>
Need device hardware access?
|-- Camera for scanning?
| +-- tauri-plugin-barcode-scanner (QR, EAN-13, etc.)
|-- Biometric authentication?
| +-- tauri-plugin-biometric (Face ID, fingerprint)
|-- NFC tags?
| +-- tauri-plugin-nfc (read/write NDEF tags)
|-- Vibration / haptic feedback?
| +-- tauri-plugin-haptics (impact, notification, selection feedback)
|-- GPS / location?
| +-- tauri-plugin-geolocation (position, altitude, heading, speed)
+-- Other device features?
+-- Check the Tauri plugin registry for mobile-compatible plugins
Is this plugin mobile-only?
|-- YES (biometric, barcode, NFC, haptics, geolocation)
| +-- Use #[cfg(mobile)] for registration
| +-- Use cfg(any(target_os = "android", target_os = "ios")) for Cargo deps
|-- NO (fs, dialog, store, notification, http, etc.)
| +-- Register unconditionally (works on both desktop and mobile)
+-- UNSURE
+-- Check plugin docs for "Supported Platforms" table
Adding a mobile plugin?
|
+-- Step 1: Tauri capability file (src-tauri/capabilities/)
| +-- Add plugin permissions (e.g., "biometric:default")
+-- Step 2: iOS Info.plist (src-tauri/Info.ios.plist)
| +-- Add NS*UsageDescription keys for each permission
+-- Step 3: Android manifest (gen/android/.../AndroidManifest.xml)
| +-- Add <uses-permission> and <uses-feature> elements
+-- Step 4: Runtime permission request
+-- Use plugin's checkPermissions() / requestPermissions() API
See reference.md for CLI command reference and mobile prerequisites checklist.
</decision_framework>
<red_flags>
High Priority Issues:
#[cfg(mobile)] -- breaks desktop builds with missing native dependencies#[cfg_attr(mobile, tauri::mobile_entry_point)] on run() -- mobile app cannot launchtauri dev instead of tauri ios dev / tauri android dev for mobile -- builds for desktop, not mobile@tauri-apps/api/tauri import path (removed in v2 -- use @tauri-apps/api/core)Medium Priority Issues:
isAvailable() before using hardware plugins (biometric, NFC) -- the device may lack hardware support@Command methods -- freezes the UIPrivacyInfo.xcprivacy for App Store compliance -- Apple rejects apps without privacy manifestscheckPermissions() / requestPermissions()) -- iOS and Android require explicit user consent for camera, location, etc.TAURI_DEV_HOST environment variable in dev server config -- physical device cannot reach dev serverCommon Mistakes:
gen/android/ or gen/apple/ that get regenerated -- changes are lost on next tauri android init / tauri ios initrustup target add aarch64-apple-ios aarch64-linux-android ...) -- compilation failsGotchas & Edge Cases:
tauri ios init and tauri ios dev require macOS with Xcode installed-C link-arg=-Wl,-z,max-page-size=16384 in .cargo/config.toml for aarch64-linux-android--open flag lifecycle: When using tauri ios dev --open or tauri android dev --open, the Tauri CLI process must stay alive -- killing it breaks the build pipelineenv(safe-area-inset-*) or a community plugin for edge-to-edge rendering</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use #[cfg(mobile)] when registering mobile-only plugins -- registering them unconditionally breaks desktop builds)
(You MUST add platform permissions (Info.plist on iOS, AndroidManifest.xml on Android) in ADDITION to Tauri capability file permissions -- missing platform permissions cause silent failures or runtime crashes)
(You MUST use #[cfg_attr(mobile, tauri::mobile_entry_point)] on pub fn run() -- without it, the app cannot launch on mobile)
(You MUST run mobile dev commands (tauri ios dev, tauri android dev) instead of tauri dev for mobile targets -- tauri dev only targets desktop)
Failure to follow these rules will cause desktop build failures, runtime permission denials, or apps that cannot launch on mobile devices.
</critical_reminders>
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions
tools
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events