水無瀬のプログラミング日記

Tauriを試してみる

はじめに

Electron代替を目指すRust製FWのTauriがバージョン1.0になり、正式リリースされたので試してみる。

TL;DR.

今回作ったコード

前提条件

Rustがinstall済みであること。
install方法は下記参照。
Prerequisites | Tauri Studio

起動するとこまで試してみる

プロジェクトの作成

create-tauri-appを使ってプロジェクトを作成する。
対話形式で色々聞かれるので好みのものを選んでいく。
今回フロントエンドのFEはAngularにした。
ので、途中からはAngular CLIの質問になっている(はず)

-> % npx create-tauri-app

We hope to help you create something special with Tauri!
You will have a choice of one of the UI frameworks supported by the greater web tech community.
This tool should get you quickly started. See our docs at https://tauri.studio/

If you haven't already, please take a moment to setup your system.
You may find the requirements here: https://tauri.studio/v1/guides/getting-started/prerequisites
    
Press any key to continue...
? What is your app name? tauri-sample
? What should the window title be? Tauri Sample
? What UI recipe would you like to add? Angular CLI (https://angular.io/cli)
? Add "@tauri-apps/api" npm package? Yes
>> Running initial command(s)
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS   [ https://sass-lang.com/documentation/syntax#scss                ]

# install部分省略

>> Updating "tauri.conf.json"
>> Running final command(s)

    Your installation completed.

    $ cd tauri-sample
    $ npm run tauri dev

動かしてみる

完了メッセージにあるコマンドで動かしてみる。

-> % npm run tauri dev

> tauri-sample@0.0.0 tauri
> tauri "dev"

     Running BeforeDevCommand (`npm run start`)
        Warn Waiting for your frontend dev server to start on http://localhost:4200/...

> tauri-sample@0.0.0 start
> ng serve

? Would you like to share anonymous usage data about this project with the Angular Team at
Google under Google’s Privacy Policy at https://policies.google.com/privacy. For more
details and how to change this setting, see https://angular.io/analytics. (y/N) N
        Warn Waiting for your frontend dev server to start on http://localhost:4200/...
        Warn Waiting for your frontend dev server to start on http://localhost:4200/...
        Warn Waiting for your frontend dev server to start on http://localhost:4200/...

動かない……
おそらくng serveした際のポリシーに同意するかどうかの回答で上手く動いていないと思われる。
同意は初回だけなので、一度npm startして別途回答してあげれば解決する。

同意後、改めてを実行し、Angular CLIで設定した場合の初期(+tauriのメッセージ)が表示されるウィンドウが立ち上がればOK。

他にも色々試してみる

ウィンドウサイズを変更する

デフォルトだと800x600になっているのを変更する。
ウィンドウサイズはsrc-tauri/tauri.config.jsontauri.windowsに記載があるのでそこを変えればOK。 (ほかは省略)

{
  "tauri": {
    "windows": [
      {
        "fullscreen": false,
        "height": 1080,
        "resizable": true,
        "title": "Tauri Sample",
        "width": 1920
      }
    ]
  }
}

Rustで記載した関数をJS側から呼び出してみる

Rustで記載したJS側から呼び出すことができる。
今回は引数受け取って値を返す簡単な例で試してみる。

Rust側の対応

jsで呼び出す関数(コマンド)はmain.rs#[tauri::command]を付けた関数を追加することで作成できる。
作成した関数をbuilderに渡せば良い。Rust側の準備はこれで完了。

#[tauri::command]
fn custom_greet(name: String) -> String {
    format!("Hello {}!", name).into()
}

fn main() {
    let context = tauri::generate_context!();
    tauri::Builder::default()
        .menu(tauri::Menu::os_default(&context.package_info().name))
        .invoke_handler(tauri::generate_handler![custom_greet])
        .run(context)
        .expect("error while running tauri application");
}

JS側の対応

jsではinvokeという関数を使い、Rustで定義した関数を呼び出す。
Rustで定義した関数に引数を渡すには、Rust側の関数の仮引数をキャメルケースにしたJSONオブジェクトをinvokeに渡してあげれば良い。
invokeの戻り値はPromise かつ unknownで返ってくるので、戻り値を使う場合はちゃんとハンドリングしてあげる必要がある。

import {invoke} from '@tauri-apps/api';

async function greet() {
  const result = await invoke('custom_greet', {name: 'Taro'});
}  

実装してみる

上記をまとめてフロントエンドで入力した値をRust側に渡し、戻ってきた値を表示するところまで作ってみる。
Rust側は上記の例のままで良いのでフロント側を記載する。

**app.component.html**


<main>
  <p>
    Please input your name.
  </p>
  <label style="display: block">
    <input type="text" [formControl]="nameForm"/>
  </label>
  <button (click)="greet()">Greet</button>
  <p>
    {{ message }}
  </p>
</main>

app.component.ts

import {Component} from '@angular/core';
import {FormControl} from '@angular/forms';
import {invoke} from '@tauri-apps/api';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  public readonly nameForm = new FormControl('');
  public message: string | null = null;

  public async greet() {
    const name = this.nameForm.value;
    if (!name) {
      throw new Error(`name is required.`);
    }
    const result = await invoke('custom_greet', {name});

    // unknownで返ってくるのでハンドリング
    if (typeof result !== 'string') {
      throw new TypeError(`result(${result}) is not string.`);
    }
    this.message = result;
  }
}

app.module.ts

import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';

import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    // ReactiveFormsModuleを追加
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

下記の通り動けばOK。

Dev Toolを開くとエラーになる?

macOSでweb viewのDev Toolを起動すると以下のようなエラーが出る。

2022-06-19 18:20:27.219 app[15153:4264805] NSWindow warning: adding an unknown subview: <WKInspectorWKWebView: xxxxxxx>. Break on NSLog to debug.
2022-06-19 18:20:27.229 app[15153:4264805] Call stack:

どうやら既知の問題らしい。
自分の環境ではクラッシュするなど動作への影響はなかったので解決を見守るのが良さそう。

NSWindow warning: adding an unknown subview · Issue #273 · tauri-apps/wry

まとめ

Rust製のデスクトップ向けFWであるTauriを試してみた。
Electronとは違いJSだけでは完結しないものの、機能的には使いやすいと感じた。
特別特定のフロントエンドFW依存しているわけでもなかったので、Electronから移行したいってなった場合も大きく困ることはなさそう。
まだリリースされたばかりなので今後に期待しつつ、現状Electronを使っているアプリ(VSCodeなど)が載せ替えて行くのか気になるところ。

参考リンク