コンテンツにスキップ
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 プラグインの開発

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.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 のみ。アクティビティが再開されたとき

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

「load」

  • いつ: プラグインが 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) {
// ここでプラグイン設定を実行
}
}

「onNewIntent」

注記: このプラグインは 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

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

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