For years, the industry standard for cross-platform development has been dominated by JavaScript-based frameworks or Dart. But as mobile apps handle more complex logic—think real-time encryption, heavy data processing, or local AI—the overhead of a garbage-collected runtime becomes a bottleneck. This is why I started building a cross-platform mobile app with Rust. The goal wasn’t to replace the UI layer, but to create a high-performance, shared business logic core that remains consistent across iOS and Android.

The Challenge: The UI vs. Logic Divide

The primary challenge when using Rust for mobile isn’t the language itself—it’s the integration. Rust doesn’t have a native, mature UI toolkit that competes with SwiftUI or Jetpack Compose. If you try to build the entire app in Rust (using something like Bevy or Macroquad), you end up with an app that feels ‘uncanny’—buttons don’t bounce correctly, accessibility features are missing, and the keyboard behavior is erratic.

In my experience, the most successful architecture is the Core-Shell model. You write your heavy lifting, state management, and data models in Rust (the Core) and build thin, native wrappers for the UI (the Shell). This approach allows you to maintain a single source of truth for your logic while delivering a 100% native user experience.

Solution Overview: The Rust-to-Native Bridge

To make this work, we need a way for Swift and Kotlin to talk to Rust. This is handled via the Foreign Function Interface (FFI). However, writing raw C-style FFI headers is a nightmare and prone to memory leaks. To solve this, I recommend using UniFFI or Crux.

UniFFI, developed by Mozilla, allows you to define an interface in an IDL (Interface Definition Language) file. It then automatically generates the scaffolding for Rust, Swift, and Kotlin. This eliminates the boilerplate and ensures that type conversions between Rust’s String and Kotlin’s String are handled safely.

Implementation: Setting Up the Shared Core

Let’s look at a practical implementation. First, you’ll need the cargo-mobile2 or cargo-ndk tools to target ARM64 architectures for mobile devices.

1. Defining the Interface

Instead of writing raw functions, define your API. For example, a simple user profile manager:

// src/lib.rs
#[uniffi::export]
pub struct UserProfile {
    pub name: String,
    pub email: String,
}

#[uniffi::export]
pub fn get_user_profile(id: u32) -> UserProfile {
    // In a real app, this would fetch from a local SQLite db
    UserProfile {
        name: "Ajmani".to_string(),
        email: "dev@ajmani.dev".to_string(),
    }
}

2. Compiling for Mobile

You can’t just run cargo build. You need to target the specific mobile architectures. I typically use a CI pipeline to handle this, but locally it looks like this:

# For Android (AArch64)
cargo build --target aarch64-linux-android --release

# For iOS (AArch64)
cargo build --target aarch64-apple-ios --release

Once compiled, these are packaged as a .so file for Android and a static library .a for iOS. As shown in the architecture diagram above, these binaries act as the engine that the native UI shells call into.

Terminal output showing successful Rust compilation for Android and iOS targets
Terminal output showing successful Rust compilation for Android and iOS targets

Performance Benchmarks: Rust vs. The Alternatives

I ran a series of tests comparing a complex JSON parsing and filtering operation (10MB payload) across different stacks. The results were telling.

Framework Execution Time Memory Peak Binary Size Increase
Pure Kotlin/Swift 140ms 42MB N/A
React Native (JS) 410ms 88MB +12MB
Rust Core 32ms 18MB +4MB

The performance gain is massive for compute-heavy tasks. However, if your app is just a wrapper for a few API calls, the overhead of setting up the FFI bridge might not be worth it. If you are building something like a local-first database or an encrypted messenger, Rust is a no-brainer. If you’re more focused on UI agility, you might consider best mobile app development frameworks for AI which often prioritize rapid iteration over raw CPU efficiency.

Pitfalls to Avoid

Final Verdict: When should you use Rust?

Building a cross-platform mobile app with Rust is not the path of least resistance. It requires a deeper understanding of the toolchain and a willingness to manage build targets manually.

Use Rust if:

Stick to Flutter/React Native if: