はじめに
TauriでAndroidアプリを開発する際、フロントエンドはWebViewベースで構築されています。そのため、OS固有のネイティブ機能にアクセスするには、まずKotlinでその機能を実装し、TypeScript、Rust、Kotlin間の橋渡し(ブリッジ)を構築する必要があります。
ただし、このブリッジの仕組みはTauriフレームワークによって提供されているため、開発者は用意された手順に従って実装するだけで、言語間の連携を実現できます。
この記事では、フロントエンドからKotlinで書かれたネイティブ機能との連携方法を紹介します。
プラグインの作成
今のところ、ネイティブ機能と連携するためには、プラグインを作成する必要があります1。
プラグインプロジェクトの生成
Tauriアプリのプロジェクトのルートディレクトリで次のコマンドを打ちます。 [name]
は任意のプラグイン名です。
npx @tauri-apps/cli plugin new [name] --android --ios
--ios
は、iOSをサポートしない場合は不要です。
コマンドに成功すると、ルートディレクトリ直下にプラグイン用のプロジェクトが生成されます。プラグイン名には自動的に tauri-plugin-
というプレフィックスが付きます。
これ以降、説明のためにプラグイン名をtauri-plugin-exampleとします。
アプリのディレクトリ構成は以下のようになります。
[tauri-app]/ <- アプリのルートディレクトリ
└ index.html
└ public/
└ src/
└ src-tauri/
└ package.json
└ ...
└ tauri-plugin-example/ <- プラグイン用のプロジェクトが生成される
プラグインの追加
上記で生成したプラグインをアプリの依存関係に追加します。
Cargo.toml
[dependencies]
+ tauri-plugin-example = { path = "../tauri-plugin-example/" }
package.json
{
"dependencies": {
+ "tauri-plugin-example-api": "file:./tauri-plugin-example"
}
}
lib.rs
src-tauri/src/lib.rs
にプラグインの初期化コードを追加します。
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
+ .plugin(tauri_plugin_example::init())
...
}
パーミッションの設定
アプリのパーミッションに追加したプラグインを設定します。パーミッションは default
にします。なお、プラグイン名に tauri-plugin-
プレフィックスは不要です。
{
"permissions": [
"core:default",
"shell:allow-open"
+ "example:default"
]
}
プラグインの実装
この記事では、例として、フロントエンドからメッセージを受け取ってToastを表示するコマンドを実装します。
ExamplePlugin.kt
tauri-plugin-example/android/src/main/java/ExamplePlugin.kt
に下記のような showToast
メソッドを実装します。メソッドには @Command
アノテーションを付けます。 ShowToastArgs
クラスはフロントエンドから渡されるデータの型を表します。
@InvokeArg
class ShowToastArgs {
var message: String? = null
/**
* "short" or "long"
*/
var length: String? = null
}
@TauriPlugin
class ExamplePlugin(private val activity: Activity) : Plugin(activity) {
@Command
fun showToast(invoke: Invoke) {
val args = invoke.parseArgs(ShowToastArgs::class.java)
args.message?.let {
val length = when (args.length) {
"short" -> Toast.LENGTH_SHORT
"long" -> Toast.LENGTH_LONG
else -> Toast.LENGTH_SHORT
}
Toast.makeText(activity, it, length).show()
}
invoke.resolve()
}
}
コマンドが正常に終了したときは invoke.resolve()
メソッドを呼びます。
- フロントエンドにデータを返したいときは
resolve
メソッドの引数にJSObject
を渡します - コマンドが失敗したときは、
resolve
メソッドの代わりにreject
メソッドを呼ぶことでエラーを返せます
ExamplePlugin
コンストラクタの引数には Activity
が渡されるため、大抵のことはできそうです。
models.rs
Kotlinで書いた ShowToastArgs
クラスに対応する形でモデルを定義します。変数名はスネークケースで書きます。
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowToastRequest {
pub message: Option<String>,
pub length: Option<String>,
}
フロントエンドにデータを返す場合、Responseのモデルもこのファイルに定義します。
mobile.rs
モバイルプラットフォーム向けに show_toast
メソッドを実装します。 run_mobile_plugin
の第1引数は、Kotlinで実装したコマンドのメソッド名です。
impl<R: Runtime> Example<R> {
pub fn show_toast(&self, payload: ShowToastRequest) -> crate::Result<()> {
self.0
.run_mobile_plugin("showToast", payload)
.map_err(Into::into)
}
}
desktop.rs
デスクトップ向けの show_toast
メソッドですが、今回、デスクトップアプリは対象外のため、メソッドだけ用意し中身は何も実装しません。
impl<R: Runtime> Example<R> {
pub fn show_toast(&self, payload: ShowToastRequest) -> crate::Result<()> {
// Nothing to do
Ok(())
}
}
commands.rs
tauri-plugin-example/src/commands.rs
に show_toast
関数を書きます。
#[command]
pub(crate) async fn show_toast<R: Runtime>(
app: AppHandle<R>,
payload: ShowToastRequest,
) -> Result<()> {
app.example().show_toast(payload)
}
lib.rs
tauri-plugin-example/src/lib.rs
の Builder
において、上記commands.rsで書いた show_toast
を追加します。
Builder::new("example")
.invoke_handler(tauri::generate_handler![
commands::ping,
+ commands::show_toast
])
build.rs
tauri-plugin-example/build.rs
の COMMANDS
に show_toast
を追加します。
const COMMANDS: &[&str] = &[
"ping",
+ "show_toast",
];
default.toml
tauri-plugin-example/permissions/default.toml
の permissions
に allow-show-toast
を追加します。
[default]
description = "Default permissions for the plugin"
permissions = ["allow-ping", "allow-show-toast"]
index.ts
下記のような showToast
関数を書きます。 payload
にセットするオブジェクトは、上記models.rsで書いた ShowToastRequest
に対応します。キー名はキャメルケースで書きます。
export async function showToast(
message: string,
length?: string,
): Promise<void> {
return await invoke("plugin:example|show_toast", {
payload: {
message,
length: length || "short",
},
});
}
tauri-plugin-example/guest-js/index.ts
に書いたexport文は、フロントエンドでimportすることができます。
import { showToast } from "tauri-plugin-example-api";
プラグインのビルド
最後にビルドすることを忘れないようにしてください。
cd tauri-plugin-example
npm run build
プラグインを使う
上記のindex.tsでエクスポートした showToast
関数をインポートして使えます。下記は使用例です。
import { showToast } from "tauri-plugin-example-api";
...
function App() {
...
// ボタンが押されたら、Toastで"Hello!"と表示する
function handleClick() {
showToast("Hello!").then(() => {
// コマンドの実行に成功したとき(resolve)
}).catch(error => {
// コマンドの実行に失敗したとき(reject)
});
}
...
}
export default App;
おわりに
フロントエンドからKotlinで書かれたネイティブ機能との連携方法を紹介しました。
実装するにあたり手数が多い印象ですが、Tauriの今後のバージョンアップで改善されることを期待しています。
参考
Develop a Tauri plugin for Android