How to Use Rust in iOS Applications

You cannot directly link Rust binaries into iOS applications because Apple's toolchain requires static libraries compiled with specific flags and architecture support that standard Rust tooling doesn't provide out of the box.

You cannot directly link Rust binaries into iOS applications because Apple's toolchain requires static libraries compiled with specific flags and architecture support that standard Rust tooling doesn't provide out of the box. Instead, you must compile Rust code into a static library (.a) using cargo-lipo or cargo-mobile and link it via a Swift or Objective-C bridge, ensuring all dependencies are compatible with iOS.

The most reliable workflow involves using cargo-lipo to build universal binaries for both device and simulator architectures, then exposing the Rust logic through a C-compatible FFI interface. Here is a practical setup:

First, create a Rust library crate with a #[no_mangle] function to ensure the symbol is visible to the linker:

// src/lib.rs
#[no_mangle]
pub extern "C" fn rust_calculate_sum(a: i32, b: i32) -> i32 {
    a + b
}

Next, install cargo-lipo to handle the multi-architecture build process required by Xcode:

cargo install cargo-lipo
# Build the universal static library for iOS
cargo lipo --release

This generates a libyour_crate.a file in the target/lipo/release directory. In your Xcode project, add this .a file to your "Link Binary With Libraries" phase and ensure the "Header Search Paths" point to the directory containing your generated header file (usually created by bindgen or manually written).

Finally, call the function from Swift using the extern keyword:

import Foundation

// Declare the external C function
@_silgen_name("rust_calculate_sum")
func rust_calculate_sum(a: Int32, b: Int32) -> Int32

// Usage
let result = rust_calculate_sum(a: 10, b: 20)
print("Result: \(result)")

Critical considerations include ensuring your Rust code does not use any features unsupported on iOS, such as std::fs for file system access (use tokio or async-std with care) or threading primitives that conflict with iOS's Grand Central Dispatch. You must also set the target in your Cargo.toml to aarch64-apple-ios for devices and x86_64-apple-ios for simulators, which cargo-lipo handles automatically. If you need complex data structures, consider using serde to serialize data to JSON or binary formats before crossing the FFI boundary, as passing complex Rust types directly to Swift is error-prone and unsupported.