Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions src/content/docs/develop/Plugins/develop-mobile.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,129 @@ Use a nullable type and set the default value on the command function instead.
Required arguments are defined as `let <argumentName>: Type`
:::

## Calling Rust From Mobile Plugins

It is often preferable to write plugin code in Rust, for performance and reusability. While Tauri doesn't directly provide a mechanism to call Rust from your plugin code, using JNI on Android and FFI on iOS allows plugins to call shared code, even when the application WebView is suspended.

### Android

In your plugin's `Cargo.toml`, add the jni crate as a dependency:

```toml
[target.'cfg(target_os = "android")'.dependencies]
jni = "0.21"
```

Load the application library statically and define native functions in your Kotlin code:

```kotlin
private const val TAG = "MyPlugin"

init {
try {
// Load the native library (libapp_lib.so)
// This is the shared library built by Cargo with crate-type = ["cdylib"]
System.loadLibrary("app_lib")
Log.d(TAG, "Successfully loaded libapp_lib.so")
} catch (e: UnsatisfiedLinkError) {
Log.e(TAG, "Failed to load libapp_lib.so", e)
throw e
}
}

external fun helloWorld(name: String): String?
```

Then in your plugin's Rust code, define the function JNI will look for:

```rust
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "system" fn Java_com_example_HelloWorld_helloWorld(
mut env: JNIEnv,
_class: JClass,
name: JString,
) -> jstring {
log::debug!("Calling JNI Hello World!");
let result = format!("Hello, {}!", name);

match env.new_string(result) {
Ok(jstr) => jstr.into_raw(),
Err(e) => {
log::error!("Failed to create JString: {}", e);
std::ptr::null_mut()
}
}
}
```

### iOS

iOS only uses standard C FFI, so doesn't need any new dependencies. Add the hook in your Swift code, as well as any necessary cleanup:

```swift
@_silgen_name("hello_world_ffi")
private static func helloWorldFFI(_ name: UnsafePointer<CChar>) -> UnsafeMutablePointer<CChar>?

@_silgen_name("free_hello_result_ffi")
private static func freeHelloResult(_ result: UnsafeMutablePointer<CChar>)

static func helloWorld(name: String) -> String? {
// Call Rust FFI
let resultPtr = name.withCString({ helloWorldFFI($0) })

// Convert C string to Swift String
let result = String(cString: resultPtr)

// Free the C string
freeHelloResult(resultPtr)

return result
}

```

Then, implement the Rust side:

```rust
#[no_mangle]
pub unsafe extern "C" fn hello_world_ffi(c_name: *const c_char) -> *mut c_char {
let name = match CStr::from_ptr(c_name).to_str() {
Ok(s) => s,
Err(e) => {
log::error!("[iOS FFI] Failed to convert C string: {}", e);
return std::ptr::null_mut();
}
};

let result = format!("Hello, {}!", name);

match CString::new(result) {
Ok(c_str) => c_str.into_raw(),
Err(e) => {
log::error!("[iOS FFI] Failed to create C string: {}", e);
std::ptr::null_mut()
}
}
}

#[no_mangle]
pub unsafe extern "C" fn free_hello_result_ffi(result: *mut c_char) {
if !result.is_null() {
drop(CString::from_raw(result));
}
}
```

## Android 16KB Memory Pages

Google is moving to make 16KB memory pages a requirement in all new Android app submissions. In order to build Tauri apps that meet this requirement, add the following to `.cargo/config.toml`:

```toml
[target.aarch64-linux-android]
rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"]
```

## Permissions

If a plugin requires permissions from the end user, Tauri simplifies the process of checking and requesting permissions.
Expand Down