モバイル・プラグインの開発
プラグインは、Kotlin(またはJava)とSwiftで記述されたネイティブ(そのまま)のモバイル・コードを実行できます。デフォルトのプラグイン・テンプレートには、Kotlin を使用した「Android ライブラリ・プロジェクト」と、Rust コードからどのように実行させるのか(トリガーするのか)を説明するモバイル・コマンドのサンプルを含んだ「Swift パッケージ」が含まれています。
プラグイン・プロジェクトの初期化
新しいプラグイン・プロジェクトを初期化するには、前章 プラグインの開発の手順に従ってください。
既存のプラグインに 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 関数の内容)を定義します }}
この実装により、コマンドと Rust コードの両方で使用可能な API を共有するプロセスが簡素化されます。
Android プラグインの開発
Android 用の Tauri プラグインは、app.tauri.plugin.Plugin
を拡張し、app.tauri.annotation.TauriPlugin
でアノテーションされた「Kotlin クラス」として定義されます。app.tauri.annotation.Command
でアノテーションされた各メソッドは、Rust または JavaScript から呼び出すことができます。
アノテーション annotation。データに対して関連する情報を注釈として付与すること。詳しくは Wikipedia を参照してください。
Tauri は、Android プラグインの実装にデフォルトで Kotlin を使用しますが、必要に応じて Java に切り替えることもできます。プラグインを生成後、Android Studio で Kotlin プラグイン・クラスを右クリックし、メニューから「Kotlin ファイルを Java ファイルに変換」オプションを選択します。Kotlin プロジェクトの Java への移行では Android Studio のガイドに従ってください。
iOS プラグインの開発
iOS 用の Tauri プラグインは、Tauri
パッケージの Plugin
クラスを拡張する Swift クラスとして定義されています。@objc
属性と (_invoke: Invoke)
パラメータを持つ各関数(たとえば @objc private func download(_invoke: Invoke) { }
)は、Rust または JavaScript から呼び出すことができます。
プラグインは Swift パッケージ として定義されているため、Swift のパッケージ・マネージャーを使用して依存関係を管理できます。
プラグインの設定
プラグイン設定の方法に関する詳細については、前章「プラグインの開発」のプラグインの設定 を参照してください。
モバイルのプラグイン・インスタンスには、プラグイン設定用の「ゲッター」コマンドがあります:
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: プラグインが Webview に読み込まれたとき
- onNewIntent: Android のみ。アクティビティが再開されたとき
前章の「プラグインの開発」には、上記以外の プラグインのライフサイクル・イベント が記載されています。
「load」
- いつ: プラグインが Webview に読み込まれたとき
- 目的: プラグイン初期化コードの実行
import android.app.Activityimport android.webkit.WebViewimport app.tauri.annotation.TauriPlugin
@TauriPluginclass ExamplePlugin(private val activity: Activity): Plugin(activity) { override fun load(webView: WebView) { // ここでプラグイン設定を実行 }}
class ExamplePlugin: Plugin { @objc public override func load(webview: WKWebView) { let timeout = self.config["timeout"] as? Int ?? 30 }}
「onNewIntent」
注記: このプラグインは Android でのみ利用可能です。
- いつ: アクティビティが再開されたとき。詳細については、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) { // 新しい「インテント」イベントを処理 }}
モバイル・コマンドの追加
それぞれのモバイル・プロジェクトには、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
// データの取得を目的としている場合は Dispatchers.IO に変更しますval 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"]) }}
Rust からモバイル・コマンドを呼び出すには、tauri::plugin::PluginHandle
を使用します。
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) { // ここでアクセス権を要求します // 続いて要求を解決します invoke.resolve(["postNotification": "granted"]) }}
Tauri は、プラグインに対する二つのコマンド checkPermissions
と requestPermissions
を自動的に実装します。
この二つのコマンドは、JavaScript または Rust から直接呼び出すことができます。
import { invoke, PermissionState } from '@tauri-apps/api/core'
interface Permissions { postNotification: PermissionState}
// アクセス権の状態を確認const permission = await invoke<Permissions>('plugin:<plugin-name>|checkPermissions')
if (permission.postNotification === 'prompt-with-rationale') { // ユーザーに対してアクセス権が必要な理由についての情報を表示する}
// アクセス権を要求if (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) { // 新しい「インテント」イベントを処理 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 );}
【※ この日本語版は、「Nov 13, 2024 英語版」に基づいています】
© 2025 Tauri Contributors. CC-BY / MIT