コンテンツにスキップ
Tauri

プラグインの開発

プラグインは、Tauri ライフサイクルにフックしたり、Webview API に依存している Rust コードを開放したり、Rust、Kotlin、Swift コードでコマンドを処理したり、その他さまざまなことを行なうことができます。

Tauriは、Webview 機能を備えたウィンドウ・システム、Rust プロセスとWebview との間のメッセージ送信方法、イベント・システム、そして開発エクスペリエンス(体験)を向上させる各種のツール群を提供します。設計上、Tauri コアにはすべての人に必要というわけではない機能は含まれていません。その代わりに、「プラグイン」と呼ばれる Tauri アプリケーションに外部機能を追加するためのメカニズムを提供しています。

Tauri プラグインは、「Cargo クレート」と、コマンドとイベントのAPIバインディングを提供するオプションの「NPM パッケージ」とで構成されています。さらに、プラグイン・プロジェクトには、Android ライブラリ・プロジェクトと iOS 用の Swift パッケージも含めることができます。Android および iOS 向けプラグイン開発の詳細については、次章 モバイル・プラグイン開発ガイド をご覧ください。

《訳注》

API バインディング 原文 API bindings。「API バインディング」は、API の呼び出し時にやりとりされる様々なデータを、API の仕様に適合するように、自動的に複雑なデータ変換処理を行なう仕組み。

命名規則

Tauri プラグインには、「プレフィックス」(接頭辞)とそれに続く「プラグイン名」が付きます。「プラグイン名」は tauri.conf.json > plugins のプラグイン設定で指定されています。

デフォルトでは、Tauri は、あなたが作成するプラグイン・クレートに tauri-plugin- という「プレフィックス」を付けます。これにより、プラグインが Tauri コミュニティ内で見付けられやすくなり、Tauri CLI でも利用しやすくなります。新しいプラグイン・プロジェクトを初期化する際には、「プラグイン名」を指定する必要があります。生成されるクレート名は tauri-plugin-{プラグイン名}、JavaScript NPM パッケージ名は tauri-plugin-{プラグイン名}-api になります(ただし、可能であれば NPM スコープ を使用することをお勧めします)。Tauri の NPM パッケージの命名規則は @scope-name/plugin-{プラグイン名} です。

プラグイン・プロジェクトの初期化

新しいプラグイン・プロジェクトをブートストラップ(起動)するには、plugin new を実行します。NPM パッケージが不要な場合は、--no-api CLI フラグを使用してください。Android または iOS をサポートするようにプラグインを初期化する場合は、--android または --ios フラグを使用してください。

インストール後、次のコマンドを実行してプラグイン・プロジェクトを作成できます:

npx @tauri-apps/cli plugin new [name]

これにより、ディレクトリ tauri-plugin-[name(プラグイン名)] でプラグインが初期化され、指定した CLI フラグに応じて、出来上がったプロジェクトは、たとえば次のようになります:

. tauri-plugin-[name(プラグイン名)]/
├── src/ - Rust のコード
│ ├── commands.rs - webview が使用できるコマンドを定義
| ├── desktop.rs - デスクトップ実装
| ├── error.rs - 結果戻り値で用いるデフォルト・エラー型
│ ├── lib.rs - 適切な実装・設定状態 ... を再転送
│ ├── mobile.rs - モバイル実装
│ └── models.rs - 共用される構造体
├── permissions/ - (作成された)コマンド用アクセス権ファイルを格納します
├── android - Android ライブラリ
├── ios - Swift パッケージ
├── guest-js - JavaScript API バインディングのソースコード
├── dist-js - guest-js から変換されたアセット
├── Cargo.toml - Cargo クレートのメタデータ
└── package.json - NPM パケージのメタデータ

既存のプラグインに Android または iOS の機能を追加したい場合は、plugin android addplugin ios add を使用してモバイル・ライブラリ・プロジェクトをブートストラップ(起動)し、必要な変更を組み込むことができます。

モバイル・プラグインの開発

プラグインは、Kotlin(またはJava)とSwiftで記述されたネイティブのモバイル・コードを実行できます。デフォルトのプラグイン・テンプレートには、Kotlin を使用した「Android ライブラリ・プロジェクト」と「Swift パッケージ」が含まれています。Rust コードからどのように実行させる(トリガーする)のかを示すモバイル・コマンドのサンプルも含みます。

モバイル用プラグイン開発の詳細については、次章の モバイル・プラグインの開発 をご覧ください。

プラグインの設定

プラグインが使用される Tauri アプリケーションでは、プラグインの設定を tauri.conf.json で行ないます。項目の plugin-name は実際の「プラグインの名前」のことです:

{
"build": { ... },
"tauri": { ... },
"plugins": {
"plugin-name": {
"timeout": 30
}
}
}

プラグインの設定は Builder にセットされ、実行時に解析されます。以下は、プラグインの設定を指定するために使用される Config 構造体の例です。

src/lib.rs
use tauri::plugin::{Builder, Runtime, TauriPlugin};
use serde::Deserialize;
// プラグインの設定を定義する
#[derive(Deserialize)]
struct Config {
timeout: usize,
}
pub fn init<R: Runtime>() -> TauriPlugin<R, Config> {
// 代わりに `Builder::<R, Option<Config>>` を使用して
// プラグインの設定をオプションにする
Builder::<R, Config>::new("<plugin-name>")
.setup(|app, api| {
let timeout = api.config().timeout;
Ok(())
})
.build()
}

ライフサイクル・イベント

プラグインは、いくつかのライフサイクル・イベントにフックできます:

  • setup: プラグインが初期化されるとき
  • on_navigation: Webview がナビゲーションを実行し始めたとき
  • on_webview_ready: 新しいウィンドウが生成されたとき
  • on_event: イベント・ループ「イベント」通知のとき
  • on_drop: プラグインが破棄されたとき

上記以外に、モバイル・プラグイン関連の ライフサイクル・イベント もあります。

「setup」

  • いつ: プラグインが初期化されるとき
  • 目的: モバイル・プラグインの登録、状態の管理、バックグラウンド・タスクの実行
src/lib.rs
use tauri::{Manager, plugin::Builder};
use std::{collections::HashMap, sync::Mutex, time::Duration};
struct DummyStore(Mutex<HashMap<String, String>>);
Builder::new("<plugin-name>")
.setup(|app, api| {
app.manage(DummyStore(Default::default()));
let app_ = app.clone();
std::thread::spawn(move || {
loop {
app_.emit("tick", ());
std::thread::sleep(Duration::from_secs(1));
}
});
Ok(())
})

「on_navigation」

  • いつ: Webview がナビゲーションを実行し始めたとき
  • 目的: ナビゲーションの検証、URL 変更の追跡

false が返されると、ナビゲーションがキャンセルされます。

src/lib.rs
use tauri::plugin::Builder;
Builder::new("<plugin-name>")
.on_navigation(|window, url| {
println!("window {} is navigating to {}", window.label(), url);
// 禁止されている場合はナビゲーションをキャンセルします
url.scheme() != "forbidden"
})

「on_webview_ready」

  • いつ: 新しいウィンドウが生成されたとき
  • 目的: すべてのウィンドウに対して初期化スクリプトを実行
src/lib.rs
use tauri::plugin::Builder;
Builder::new("<plugin-name>")
.on_webview_ready(|window| {
window.listen("content-loaded", |event| {
println!("webview content has been loaded");
});
})

「on_event」

  • いつ: イベント・ループ「イベント」通知のとき
  • 目的: ウィンドウ・イベント、メニュー・イベント、アプリケーション終了要求などのコア・イベントを処理

このライフサイクル・フックを使用すると、あらゆるイベント・ループの「イベント」通知を受け取ることができます。

src/lib.rs
use std::{collections::HashMap, fs::write, sync::Mutex};
use tauri::{plugin::Builder, Manager, RunEvent};
struct DummyStore(Mutex<HashMap<String, String>>);
Builder::new("<plugin-name>")
.setup(|app, _api| {
app.manage(DummyStore(Default::default()));
Ok(())
})
.on_event(|app, event| {
match event {
RunEvent::ExitRequested { api, .. } => {
// ユーザーがウィンドウを閉じるように要求し、ウィンドウが残っていません
// アプリの終了を防ぎます:
api.prevent_exit();
}
RunEvent::Exit => {
// アプリが終了します。ここでクリーンアップします。
let store = app.state::<DummyStore>();
write(
app.path().app_local_data_dir().unwrap().join("store.json"),
serde_json::to_string(&*store.0.lock().unwrap()).unwrap(),
)
.unwrap();
}
_ => {}
}
})

「on_drop」

  • いつ: プラグインが破棄されたとき
  • 目的: プラグインが破棄されたときにコードを実行

詳細については、Drop を参照してください。

src/lib.rs
use tauri::plugin::Builder;
Builder::new("<plugin-name>")
.on_drop(|app| {
// プラグインが破棄されました...
})

Rust APIs の開放

《訳注》

開放 原文は expose(暴露する、露呈する、曝す、公開する)。本稿では「〜から見られるようにする」という観点から主に「開放」という訳語をあてています。

プロジェクトの desktop.rs および mobile.rs で定義されたプラグイン API は、プラグインと同じ名前(「パスカル・ケース」で表記)を持つ構造体としてユーザーにエクスポートされます。プラグインがセットアップされると、この構造体のインスタンスが作成され、「状態」として管理されます。これにより、ユーザーはプラグインに定義された拡張トレイトを通じて、いつでも Manager インスタンス(たとえば AppHandleAppWindow など)を使用してこの構造体を取得できます。

《訳注》

パスカル・ケース pascal case。変数名などを複合語で表記する際の命名規則のひとつで、各単語の先頭の文字を 「PascalCase」のように大文字にする形式。なお、同様の表記法に「キャメル・ケース camelCase」があるが、キャメル・ケースでは、先頭の単語は大文字を用いない。詳しくは、Wikipedia の「キャメルケース を参照してください。

たとえば、global-shortcut plugin は、GlobalShortcutExt トレイトの global_shortcut メソッドを使用して読み取ることができる GlobalShortcut 構造体を定義します:

src-tauri/src/lib.rs
use tauri_plugin_global_shortcut::GlobalShortcutExt;
tauri::Builder::default()
.plugin(tauri_plugin_global_shortcut::init())
.setup(|app| {
app.global_shortcut().register(...);
Ok(())
})

コマンドの追加

コマンド(命令)は commands.rs ファイルで定義されています。これらは通常の Tauri アプリケーションのコマンドです。アプリケーション・コマンドと同じように、AppHandle および Window の各インスタンスに直接アクセスし、「状態」を確認し、入力を取得できます。Tauri コマンドの詳細については、前述の「フロントエンドから Rust を呼び出す」の章にある コマンド・ガイド をお読みください。

次のコマンドは、依存性の注入 を介して AppHandleWindow インスタンスにアクセスする方法を示しており、二つの入力パラメータ (on_progressurl)を受け取ります。

src/commands.rs
use tauri::{command, ipc::Channel, AppHandle, Runtime, Window};
#[command]
async fn upload<R: Runtime>(app: AppHandle<R>, window: Window<R>, on_progress: Channel, url: String) {
// ここにコマンド・ロジックを実装します
on_progress.send(100).unwrap();
}

コマンドを Webview に開放するには、lib.rsinvoke_handler() 呼び出しにフックする必要があります。

src/lib.rs
Builder::new("<plugin-name>")
.invoke_handler(tauri::generate_handler![commands::upload])

プラグイン・ユーザーが JavaScript でコマンドを簡単に呼び出せるように、webview-src/index.ts にバインディング関数を定義してください:

import { invoke, Channel } from '@tauri-apps/api/core'
export async function upload(url: string, onProgressHandler: (progress: number) => void): Promise<void> {
const onProgress = new Channel<number>()
onProgress.onmessage = onProgressHandler
await invoke('plugin:<plugin-name>|upload', { url, onProgress })
}

テストする前に必ず TypeScript コードをビルドしてください。

コマンドのアクセス権

デフォルトでは、フロントエンドからコマンドにアクセスできません。コマンドのいずれかを実行しようとすると、「拒否」エラーが発生します。実際にコマンドを利用できるようにするには、各コマンドを許可するアクセス権も定義する必要があります。

アクセス権ファイル

アクセス権は、permissions ディレクトリ内の JSON または TOML ファイルとして定義されています。各ファイルでは、アクセス権のリスト、アクセス権セットのリスト、およびプラグインのデフォルトのアクセス権を定義できます。

アクセス権

アクセス権はプラグイン・コマンドの権限を定義します。コマンドのリストを「許可」または「拒否」できたり、コマンド固有のスコープ(適用範囲)とグローバル・スコープを関連付けたりすることができます。

permissions/start-server.toml
"$schema" = "schemas/schema.json"
[[permission]]
identifier = "allow-start-server"
description = "Enables the start_server command."
commands.allow = ["start_server"]
[[permission]]
identifier = "deny-start-server"
description = "Denies the start_server command."
commands.deny = ["start_server"]
スコープ(適用範囲)

スコープを使用すると、プラグインは個々のコマンドに対してより詳細な制限を定義できます。 それぞれのアクセス権には、コマンド毎に、またはプラグイン全体に対して、「許可」または「拒否」される内容を定義するスコープ・オブジェクトのリストを定義します。

では、shell プラグインで生成が「許可」されるバイナリ・リスト用のスコープ・データを保持する構造体サンプルを定義してみましょう:

src/scope.rs
#[derive(Debug, schemars::JsonSchema)]
pub struct Entry {
pub binary: String,
}
コマンド・スコープ

プラグインの利用者(コンシューマー)は、自分の「セキュリティ設定」ファイルで、特定のコマンドのスコープを定義できます(詳しくは 英語版ドキュメント を参照してください)。 コマンド固有のスコープは、tauri::ipc::CommandScope 構造体を使用して読み取ることができます。

《訳注》

プラグインの利用者(コンシューマー) plugin consumer。「コンシューマー」は消費者の意味ですが、本稿では読み易さを考え「利用者」としています。consumer という語が用いられている理由は「プラグインの使用者が必ずしも人間ではない〔サービスを消費するのが者/コマンド/システム等々である〕ため」との説明が GitHub にあります。

src/commands.rs
use tauri::ipc::CommandScope;
use crate::scope::Entry;
async fn spawn<R: tauri::Runtime>(app: tauri::AppHandle<R>, command_scope: CommandScope<'_, Entry>) -> Result<()> {
let allowed = command_scope.allows();
let denied = command_scope.denies();
todo!()
}
グローバル・スコープ

アクセス権に、「許可」または「拒否」するコマンドが定義されていない場合は「スコープ権限」とみなされ、プラグインにはグローバル・スコープのみが定義されます:

《訳注》

スコープ権限 正規訳不明。原文 it’s considered a scope permission(文意不詳)。

permissions/spawn-node.toml
[[permission]]
identifier = "allow-spawn-node"
description = "This scope permits spawning the `node` binary."
[[permission.scope.allow]]
binary = "node"

グローバル・スコープは、tauri::ipc::GlobalScope 構造体を使用して読み取ることができます。

src/commands.rs
use tauri::ipc::GlobalScope;
use crate::scope::Entry;
async fn spawn<R: tauri::Runtime>(app: tauri::AppHandle<R>, scope: GlobalScope<'_, Entry>) -> Result<()> {
let allowed = scope.allows();
let denied = scope.denies();
todo!()
}
スキーマ(データ構造定義)

スコープ・エントリ(スコープ設定)には、プラグイン利用者(コンシューマー)がスコープの形式を認識し、IDE で自動補完できるように、「JSON スキーマ」を生成するための schemars 依存関係が必要です。

《訳注》

JSON スキーマ JSON schema。JSONデータの構造(キー、値、オブジェクト、配列、データ型、制約などの要素)を定義し、データ形式に適合しているかを検証するためのツール。

スキーマを定義するには、最初に Cargo.toml ファイルに依存関係を追加します。

# scope.rs モジュールはアプリ・コードとビルド・スクリプトとの間で共有されるため、依存関係とビルド依存関係の両方に schemars を追加する必要があります。
[dependencies]
schemars = "0.8"
[build-dependencies]
schemars = "0.8"

ビルド・スクリプトに以下のコードを追加します:

build.rs
#[path = "src/scope.rs"]
mod scope;
const COMMANDS: &[&str] = &[];
fn main() {
tauri_plugin::Builder::new(COMMANDS)
.global_scope_schema(schemars::schema_for!(scope::Entry))
.build();
}
アクセス権のセット

「アクセス権のセット」とは、ユーザーがより高い抽象化レベルでプラグインを管理できる個々のアクセス権のグループです。 たとえば、ひとつの API が複数のコマンドを使用したり、あるいはコマンドのコレクションに論理的な関係があるような場合には、そのようなコマンドを含んだセットを定義する必要があります:

permissions/websocket.toml
"$schema" = "schemas/schema.json"
[[set]]
identifier = "allow-websocket"
description = "Allows connecting and sending messages through a WebSocket"
permissions = ["allow-connect", "allow-send"]
デフォルトのアクセス権

「デフォルトのアクセス権」とは、識別子「default」を持つ特別なアクセス権のセットです。必要なコマンドはデフォルトで有効化しておくことをお勧めします。 たとえば、http プラグインは、request コマンドが許可されていないと役に立ちません。

permissions/default.toml
"$schema" = "schemas/schema.json"
[default]
description = "Allows making HTTP requests"
permissions = ["allow-request"]

自動生成されたアクセス権

各コマンドのアクセス権を定義する最も簡単な方法は、build.rs ファイルで定義されたプラ​​グインのビルド・スクリプトにある「自動生成」オプションを使用することです。 定数「COMMANDS」内で、コマンドのリストを「snake_case」(スネークケース)で定義します(コマンド関数名と一致する必要があります)。すると Tauri は自動的に allow-$commandnamedeny-$commandname のアクセス権限を生成します。

次の例では、allow-upload および deny-upload のアクセス権限を生成しています:

src/commands.rs
const COMMANDS: &[&str] = &["upload"];
fn main() {
tauri_plugin::Builder::new(COMMANDS).build();
}

詳細については、目次 Security の部の「アクセス権 Permissions」の章を参照してください。

「状態」の管理

プラグインは Tauri アプリケーションと同じように、「状態 state」を管理できます。詳しくは、目次 Develop の部の「状態管理」の章をご覧ください。

【※ この日本語版は、「Nov 2, 2024 英語版」に基づいています】


© 2025 Tauri Contributors. CC-BY / MIT