行動外掛程式開發
外掛程式可以執行以 Kotlin (或 Java) 和 Swift 編寫的原生行動程式碼。預設外掛程式範本包含一個使用 Kotlin 的 Android 程式庫專案和一個包含範例行動命令的 Swift 套件,展示如何從 Rust 程式碼觸發其執行。
初始化外掛程式專案
請按照外掛程式開發指南中的步驟初始化新的外掛程式專案。
如果您有現有的外掛程式並希望為其新增 Android 或 iOS 功能,您可以使用 plugin android init
和 plugin ios init
來引導行動程式庫專案,並引導您完成所需的變更。
預設外掛程式範本將外掛程式的實作拆分為兩個獨立的模組:desktop.rs
和 mobile.rs
。
桌面實作使用 Rust 程式碼來實作功能,而行動實作則將訊息傳送至原生行動程式碼以執行函式並取回結果。如果跨兩個實作都需要共用邏輯,則可以在 lib.rs
中定義
use tauri::Runtime;
impl<R: Runtime> <plugin-name><R> { pub fn do_something(&self) { // do something that is a shared implementation between desktop and mobile }}
此實作簡化了共用 API 的流程,該 API 可供命令和 Rust 程式碼使用。
開發 Android 外掛程式
Android 的 Tauri 外掛程式定義為擴展 app.tauri.plugin.Plugin
的 Kotlin 類別,並使用 app.tauri.annotation.TauriPlugin
註解。每個使用 app.tauri.annotation.Command
註解的方法都可以由 Rust 或 JavaScript 呼叫。
Tauri 預設使用 Kotlin 進行 Android 外掛程式實作,但如果您願意,也可以切換到 Java。產生外掛程式後,在 Android Studio 中右鍵點擊 Kotlin 外掛程式類別,然後從選單中選擇「Convert Kotlin file to Java file」選項。Android Studio 將引導您完成專案遷移到 Java。
開發 iOS 外掛程式
iOS 的 Tauri 外掛程式定義為從 Tauri
套件擴展 Plugin
類別的 Swift 類別。每個具有 @objc
屬性和 (_ invoke: Invoke)
參數的函式(例如 @objc private func download(_ invoke: Invoke) { }
)都可以由 Rust 或 JavaScript 呼叫。
該外掛程式定義為 Swift 套件,以便您可以使用其套件管理器來管理依賴項。
外掛程式設定
有關開發外掛程式設定的更多詳細資訊,請參閱外掛程式開發指南的外掛程式設定章節。
行動裝置上的外掛程式實例具有外掛程式設定的 getter
import android.app.Activityimport android.webkit.WebViewimport app.tauri.annotation.TauriPluginimport app.tauri.annotation.InvokeArg
@InvokeArgclass Config { var timeout: Int? = 3000}
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { private var timeout: Int? = 3000
override fun load(webView: WebView) { getConfig(Config::class.java).let { this.timeout = it.timeout } }}
struct Config: Decodable { let timeout: Int?}
class ExamplePlugin: Plugin { var timeout: Int? = 3000
@objc public override func load(webview: WKWebView) { do { let config = try parseConfig(Config.self) self.timeout = config.timeout } catch {} }}
生命週期事件
外掛程式可以掛鉤到幾個生命週期事件
- load:當外掛程式載入到 web view 時
- onNewIntent:僅限 Android,當 activity 重新啟動時
外掛程式開發指南中還有外掛程式的其他生命週期事件。
load
- 何時:當外掛程式載入到 web view 時
- 原因:執行外掛程式初始化程式碼
import android.app.Activityimport android.webkit.WebViewimport app.tauri.annotation.TauriPlugin
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { override fun load(webView: WebView) { // perform plugin setup here }}
class ExamplePlugin: Plugin { @objc public override func load(webview: WKWebView) { let timeout = self.config["timeout"] as? Int ?? 30 }}
onNewIntent
注意:這僅適用於 Android。
- 何時:當 activity 重新啟動時。請參閱 Activity#onNewIntent 以取得更多資訊。
- 原因:處理應用程式重新啟動,例如當點擊通知或存取深度連結時。
import android.app.Activityimport android.content.Intentimport app.tauri.annotation.TauriPlugin
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { override fun onNewIntent(intent: Intent) { // handle new intent event }}
新增行動命令
在各自的行動專案中,有一個外掛程式類別,可以在其中定義可由 Rust 程式碼呼叫的命令
import android.app.Activityimport app.tauri.annotation.Commandimport app.tauri.annotation.TauriPlugin
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { @Command fun openCamera(invoke: Invoke) { val ret = JSObject() ret.put("path", "/path/to/photo.jpg") invoke.resolve(ret) }}
如果您想使用 Kotlin suspend
函式,則需要使用自訂協程範圍
import android.app.Activityimport app.tauri.annotation.Commandimport app.tauri.annotation.TauriPlugin
// Change to Dispatchers.IO if it is intended for fetching dataval scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { @Command fun openCamera(invoke: Invoke) { scope.launch { openCameraInner(invoke) } }
private suspend fun openCameraInner(invoke: Invoke) { val ret = JSObject() ret.put("path", "/path/to/photo.jpg") invoke.resolve(ret) }}
class ExamplePlugin: Plugin { @objc public func openCamera(_ invoke: Invoke) throws { invoke.resolve(["path": "/path/to/photo.jpg"]) }}
使用 tauri::plugin::PluginHandle
從 Rust 呼叫行動命令
use std::path::PathBuf;use serde::{Deserialize, Serialize};use tauri::Runtime;
#[derive(Serialize)]#[serde(rename_all = "camelCase")]pub struct CameraRequest { quality: usize, allow_edit: bool,}
#[derive(Deserialize)]pub struct Photo { path: PathBuf,}
impl<R: Runtime> <plugin-name;pascal-case><R> { pub fn open_camera(&self, payload: CameraRequest) -> crate::Result<Photo> { self .0 .run_mobile_plugin("openCamera", payload) .map_err(Into::into) }}
命令引數
引數會序列化為命令,並且可以使用 Invoke::parseArgs
函式在行動外掛程式上進行解析,並採用描述引數物件的類別。
Android
在 Android 上,引數定義為使用 @app.tauri.annotation.InvokeArg
註解的類別。內部物件也必須註解
import android.app.Activityimport android.webkit.WebViewimport app.tauri.annotation.Commandimport app.tauri.annotation.InvokeArgimport app.tauri.annotation.TauriPlugin
@InvokeArginternal class OpenAppArgs { lateinit var name: String var timeout: Int? = null}
@InvokeArginternal class OpenArgs { lateinit var requiredArg: String var allowEdit: Boolean = false var quality: Int = 100 var app: OpenAppArgs? = null}
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { @Command fun openCamera(invoke: Invoke) { val args = invoke.parseArgs(OpenArgs::class.java) }}
iOS
在 iOS 上,引數定義為繼承 Decodable
的類別。內部物件也必須繼承 Decodable 協定
class OpenAppArgs: Decodable { let name: String var timeout: Int?}
class OpenArgs: Decodable { let requiredArg: String var allowEdit: Bool? var quality: UInt8? var app: OpenAppArgs?}
class ExamplePlugin: Plugin { @objc public func openCamera(_ invoke: Invoke) throws { let args = try invoke.parseArgs(OpenArgs.self)
invoke.resolve(["path": "/path/to/photo.jpg"]) }}
權限
如果外掛程式需要最終使用者的權限,Tauri 簡化了檢查和請求權限的流程。
首先定義所需權限的清單,以及在程式碼中識別每個群組的別名。這在外掛程式 TauriPlugin
註解內完成
@TauriPlugin( permissions = [ Permission(strings = [Manifest.permission.POST_NOTIFICATIONS], alias = "postNotification") ])class ExamplePlugin(private val activity: Activity): Plugin(activity) { }
首先覆寫 checkPermissions
和 requestPermissions
函式
class ExamplePlugin: Plugin { @objc open func checkPermissions(_ invoke: Invoke) { invoke.resolve(["postNotification": "prompt"]) }
@objc public override func requestPermissions(_ invoke: Invoke) { // request permissions here // then resolve the request invoke.resolve(["postNotification": "granted"]) }}
Tauri 自動為外掛程式實作兩個命令:checkPermissions
和 requestPermissions
。這些命令可以直接從 JavaScript 或 Rust 呼叫
import { invoke, PermissionState } from '@tauri-apps/api/core'
interface Permissions { postNotification: PermissionState}
// check permission stateconst permission = await invoke<Permissions>('plugin:<plugin-name>|checkPermissions')
if (permission.postNotification === 'prompt-with-rationale') { // show information to the user about why permission is needed}
// request permissionif (permission.postNotification.startsWith('prompt')) { const state = await invoke<Permissions>('plugin:<plugin-name>|requestPermissions', { permissions: ['postNotification'] })}
use serde::{Serialize, Deserialize};use tauri::{plugin::PermissionState, Runtime};
#[derive(Deserialize)]#[serde(rename_all = "camelCase")]struct PermissionResponse { pub post_notification: PermissionState,}
#[derive(Serialize)]#[serde(rename_all = "camelCase")]struct RequestPermission { post_notification: bool,}
impl<R: Runtime> Notification<R> { pub fn request_post_notification_permission(&self) -> crate::Result<PermissionState> { self.0 .run_mobile_plugin::<PermissionResponse>("requestPermissions", RequestPermission { post_notification: true }) .map(|r| r.post_notification) .map_err(Into::into) }
pub fn check_permissions(&self) -> crate::Result<PermissionResponse> { self.0 .run_mobile_plugin::<PermissionResponse>("checkPermissions", ()) .map_err(Into::into) }}
外掛程式事件
外掛程式可以使用 trigger
函式在任何時間點發射事件
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { override fun load(webView: WebView) { trigger("load", JSObject()) }
override fun onNewIntent(intent: Intent) { // handle new intent event if (intent.action == Intent.ACTION_VIEW) { val data = intent.data.toString() val event = JSObject() event.put("data", data) trigger("newIntent", event) } }
@Command fun openCamera(invoke: Invoke) { val payload = JSObject() payload.put("open", true) trigger("camera", payload) }}
class ExamplePlugin: Plugin { @objc public override func load(webview: WKWebView) { trigger("load", data: [:]) }
@objc public func openCamera(_ invoke: Invoke) { trigger("camera", data: ["open": true]) }}
然後可以使用 addPluginListener
輔助函式從 NPM 套件呼叫輔助函式
import { addPluginListener, PluginListener } from '@tauri-apps/api/core';
export async function onRequest( handler: (url: string) => void): Promise<PluginListener> { return await addPluginListener( '<plugin-name>', 'event-name', handler );}
© 2025 Tauri Contributors。CC-BY / MIT