EmotionTechテックブログ

株式会社エモーションテックのProduct Teamのメンバーが、日々の取り組みや技術的なことを発信していくブログです。

Tauriを使ってデスクトップアプリケーションを作ってみた

こんにちは、りーです。エモーションテックのバックエンドエンジニアです。

今回はRustを使用してデスクトップアプリケーションの開発に挑戦しました。このプロジェクトでは軽量なRustフレームワークであるTauriを利用しました。

私の目標は、多くのAPIを組み合わせて、社内のチームに便益をもたらすデスクトップアプリケーションを提供することです。デスクトップアプリケーションの開発においては経験が浅く、フロントエンド開発も普段はあまりやりませんが、JavaScriptとHTMLの基礎知識を活かし、このプロジェクトに向けて熱意を持って取り組んでいます。 また、入社以来、ずっとRustを使用してプログラミングを行ってきました。このRustの経験が、今回のデスクトップアプリケーションの開発において、新たな挑戦に自信を持つ要因となることでしょう。

このプロジェクトに向けて、情熱と専門知識を結集し、技術の探求を続けます。

Tauri とは

Tauriは、クロスプラットフォームのデスクトップアプリケーションを開発するためのオープンソースフレームワークです。Tauriは、RustとJavaScript/TypeScriptを使用して開発することができます。このフレームワークは、デスクトップアプリケーションを複数のOS(WindowsmacOSLinuxなど)で実行するためのネイティブバイナリを生成します。

Tauriの主な特徴として以下の点が挙げられます:

セキュリティ: Tauriはセキュリティを重視しており、デスクトップアプリケーションを安全に実行するためのツールやガイドラインを提供しています。

プラグインサポート: Tauriはプラグインをサポートし、既存のWebテクノロジーを活用して、アプリケーションに機能を追加できます。

パフォーマンス: Rustの使用により、Tauriアプリケーションは高いパフォーマンスを提供し、リソース効率が良いです。

クロスプラットフォーム: TauriはさまざまなOSで動作し、クロスプラットフォームアプリケーションの開発を容易にします。

Tauriは、ウェブ開発者が簡単にデスクトップアプリケーションを構築するためのツールとして人気があります。ウェブ技術を使用することで、既存のスキルを活用してデスクトップアプリケーションを開発できるため、多くの開発者にとって魅力的な選択肢となっています。

今回作成するアプリケーションイメージ

UI は HTML、CSS、JS で開発することができ、Rust application 内の webview controller を介して、webview としてレンダリングするイメージです。

開発環境をセットアップ

cargo install tauri-cli

cargo install create-tauri-app --locked

プロジェクトを作成する

cargo create-tauri-app

⇧のcomnand を打って、Project nameを入力し、パッケージマネージャを選択する。 最後UIのtemplate を選択する

今回作った例です。

cargo tauri dev を実行すると、簡単な画面が表示されました。

ログイン画面作成してみる

ディレクトリ構成は以下のようになります

webviewの作成

index.html:

<!doctype html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <link rel="stylesheet" href="styles.css" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>XXX Desktop App</title>
   <script type="module" src="./index.js" defer></script>
   <style>
     .logo.vanilla:hover {
       filter: drop-shadow(0 0 2em #ffe21c);
     }
   </style>
 </head>


 <body>
   <div class="container">
     <h1>XXX Desktop App</h1>


     <h2>LOGIN</h2>
     <div id="error-message" style="color: red;"></div>


     <form class="row" id="login-form">
       <div>
         <label for="email">Email</label>
         <input id="email" placeholder="Enter a email..." />
       </div>
       <div>
         <label for="password">Password</label>
         <input id="password" type="password" placeholder="Enter a password..." />
       </div>
       <div>
         <button type="submit">Login</button>
       </div>
     </form>
   </div>
 </body>
</html>

index.js:

const { invoke } = window.__TAURI__.tauri;


let emailInputEl;
let passwordInputEl;


async function login() {
   try {
       const output = await invoke('login', {
           email: emailInputEl.value,
           password: passwordInputEl.value,
       });


       sessionStorage.setItem('token', output.authToken);
       localStorage.setItem('email', emailInputEl.value);
       window.location.href = 'list-xxx.html';
   } catch (error) {
       const errorMessageElement = document.getElementById('error-message');
       errorMessageElement.textContent = error;
   }
}


window.addEventListener('DOMContentLoaded', () => {
   emailInputEl = document.querySelector('#email');
   passwordInputEl = document.querySelector('#password');


   if (localStorage.getItem('email')) {
       emailInputEl.value = localStorage.getItem('email');
   }


   document.querySelector('#login-form').addEventListener('submit', (e) => {
       e.preventDefault();
       login();
   });
});

rust application の作成

use crate::auth::auth_api_client::AuthApiClient;
use crate::auth::auth_service::{IAuthService, LoginInput};


#[tauri::command]
pub async fn login(
   email: String,
   password: String,
   auth_api: tauri::State<'_, AuthApiClient>,
) -> Result<String, String> {
   match auth_api.login(LoginInput { email, password }).await {
       Ok(output) => Ok(output.auth_token),
       Err(e) => Err(format!("Login failed: {}", e.to_string())),
   }
}
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]


mod auth;
mod handler;


use crate::auth::auth_api_client::AuthApiClient;
use crate::handler::login_handler::login;


use reqwest::Client;
use std::time::Duration;


fn main() {
   let auth_api_client = AuthApiClient::new(
       Client::builder()
           .timeout(Duration::from_millis(10000))
           .build()
           .unwrap()
           .into(),
   );


   tauri::Builder::default()
       .manage(auth_api_client)
       .invoke_handler(tauri::generate_handler![login])
       .run(tauri::generate_context!())
       .expect("error while running XXX Desktop App");
}

cargo tauri dev を実行すると、↓画面が表示されました。

おわりに

Tauriを使ってデスクトップアプリケーションを開発する過程で、いくつかのポイントが印象的でした。一部は簡単に感じたこともあり、一部は少しハードルが高かったです。

簡単だった点

Web技術の活用: TauriはWeb技術をベースにしているため、ウェブ開発の知識を持つ人にとって、非常に取り組みやすい環境でした。HTML、CSSJavaScriptを使用してアプリケーションのUIを構築できることは大きな利点でした。

プラグインの利用: Tauriは多くのプラグインを提供しており、これらを使用することで、アプリケーションに機能を追加することが容易でした。例えば、ファイルシステムへのアクセス、ネイティブダイアログの表示、システム通知の送信など、多くの一般的なタスクがプラグインで提供されています。

コミュニティのサポート: Tauriのコミュニティは非常に活発で、質問に対する迅速なサポートを受けることができました。また、多くのチュートリアルやサンプルコードが提供されており、学習の助けになりました。

難しかった点

Rustとの統合: Rustの知識がない場合、TauriとRustを組み合わせて使うことは少し学習コストがかかるかもしれません。しかし、Rustは非常にパワフルで安全なプログラミング言語であり、学ぶ価値があると感じました。

ビルドとパッケージング: デスクトップアプリケーションのビルドとパッケージングは、プラットフォームごとに異なる手順が必要です。これはやや複雑で、特に初めての開発者にとっては調査と実践が必要でした。

クロスプラットフォームの適応: クロスプラットフォームアプリケーションを開発する場合、異なるオペレーティングシステムごとにテストと調整が必要です。これには時間と労力がかかることがあります。

総括すると、Tauriはモダンなデスクトップアプリケーションを開発するための魅力的なツールであり、ウェブ開発者にとっては親しみやすいものです。一方で、一部の複雑な側面があるため、学習と実践が必要です。しかし、それを乗り越える価値があり、高度なデスクトップアプリケーションを構築する手助けとなるでしょう。私はTauriを試してみることをお勧めします。