コンテンツにスキップ
Tauri

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

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

新しいプラグイン・プロジェクトを初期化するには、前章 プラグインの開発の手順に従ってください。

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

デフォルトのプラグイン・テンプレートは、プラグインの実装を desktop.rsmobile.rs という二つの別々のモジュールに分割します。

「デスクトップ実装」では Rust コードを用いて機能を実装しますが、「モバイル実装」ではネイティブ・モバイル・コードにメッセージを送信して関数を実行して結果を取得します。両方の実装で共通のロジックが必要な場合は、lib.rs で定義します:

src/lib.rs
use tauri::Runtime;
impl<R: Runtime> <plugin-name><R> {
pub fn do_something(&self) {
// ここにデスクトップとモバイルの間で共有される実装(この例では do_something 関数の内容)を定義します
}
}

この実装により、コマンドと Rust コードの両方で使用可能な API を共有するプロセスが簡素化されます。

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 用の Tauri プラグインは、Tauri パッケージの Plugin クラスを拡張する Swift クラスとして定義されています。@objc 属性と (_invoke: Invoke) パラメータを持つ各関数(たとえば @objc private func download(_invoke: Invoke) { } )は、Rust または JavaScript から呼び出すことができます。

プラグインは Swift パッケージ として定義されているため、Swift のパッケージ・マネージャーを使用して依存関係を管理できます。

プラグイン設定の方法に関する詳細については、前章「プラグインの開発」のプラグインの設定 を参照してください。

モバイルのプラグイン・インスタンスには、プラグイン設定用の「ゲッター」コマンドがあります:

import android.app.Activity
import android.webkit.WebView
import app.tauri.annotation.TauriPlugin
import app.tauri.annotation.InvokeArg
@InvokeArg
class Config {
var timeout: Int? = 3000
}
@TauriPlugin
class 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
}
}
}

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

  • load: プラグインが Webview に読み込まれたとき
  • onNewIntent: Android のみ。アクティビティが再開されたとき

前章の「プラグインの開発」には、上記以外の プラグインのライフサイクル・イベント が記載されています。

  • いつ: プラグインが Webview に読み込まれたとき
  • 目的: プラグイン初期化コードの実行
import android.app.Activity
import android.webkit.WebView
import app.tauri.annotation.TauriPlugin
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun load(webView: WebView) {
// ここでプラグイン設定を実行
}
}

注記: このプラグインは Android でのみ利用可能です。

  • いつ: アクティビティが再開されたとき。詳細については、Activity#onNewIntent を参照してください。
  • 目的: 「通知」がクリックされたときや「ディープリンク」にアクセスしたときなどに、アプリケーションの再起動を処理
import android.app.Activity
import android.content.Intent
import app.tauri.annotation.TauriPlugin
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun onNewIntent(intent: Intent) {
// 新しい「インテント」イベントを処理
}
}

それぞれのモバイル・プロジェクトには、Rust コードから呼び出し可能なコマンドを定義できるプラグイン・クラスがあります:

import android.app.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
@TauriPlugin
class 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.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
// データの取得を目的としている場合は Dispatchers.IO に変更します
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
@TauriPlugin
class 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)
}
}

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では、引数は @app.tauri.annotation.InvokeArg でアノテーションされたクラスとして定義されます。内部オブジェクトにもアノテーションを付与する必要があります:

import android.app.Activity
import android.webkit.WebView
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
@InvokeArg
internal class OpenAppArgs {
lateinit var name: String
var timeout: Int? = null
}
@InvokeArg
internal class OpenArgs {
lateinit var requiredArg: String
var allowEdit: Boolean = false
var quality: Int = 100
var app: OpenAppArgs? = null
}
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
@Command
fun openCamera(invoke: Invoke) {
val args = invoke.parseArgs(OpenArgs::class.java)
}
}

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) { }

Tauri は、プラグインに対する二つのコマンド checkPermissionsrequestPermissions を自動的に実装します。 この二つのコマンドは、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'] })
}

プラグインは、trigger 関数を使用していつでもイベントを発行できます:

@TauriPlugin
class 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)
}
}

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