Angular,  Nx, Rust, Tauri で始めるデスクトップアプリ開発入門

https://qiita.com/advent-calendar/2024/angular

これは Angular Advent Calendar 2024 の 15 日目の記事です。 
昨日 12/14(土) は [@scrpgil さん](https://qiita.com/scrpgil)が執筆されました。

## はじめに  

この記事では、**Angular, Nx, Rust, Tauri** を使ったデスクトップアプリを構築する方法を紹介します。  

### 対象読者  
- **Angular** や Web 開発の経験がある方  
- **Nx** を使ったモノレポ管理に興味のある方  
- **Rust** を使った高性能なロジック実装に挑戦したい方  
- **Tauri** を使ったデスクトップアプリ開発を学びたい方  

### 魅力ポイント  
- **Angular**: 使い慣れた技術でフロントエンドを構築可能  
- **Nx**: モノレポで複数プロジェクトを効率的に管理  
- **Rust**: 安全で高速なロジック実装が可能  
- **Tauri**: 軽量なクロスプラットフォームアプリを提供  

### 前提条件

下記環境が整っている事を前提とします。

<details>
<summary>開発環境</summary>

筆者のマシン環境は、 M1 Mac Book Pro Max 2020 です。
Windows, Linux での開発環境構築は未確認です。

1. Xcode のインストール
1. Rust のインストール
1. フロンエンド開発環境構築
1. 執筆時点での環境

```batch
$ npm -v       # 10.8.3  
$ node -v      # v22.9.0  
$ yarn -v      # 1.22.22  
$ pnpm -v      # 9.12.1  
$ rustc --version  # rustc 1.83.0  
$ rustup --version # rustup 1.27.1  
```

- Rust環境構築については、こちらの[記事](https://zenn.dev/icck/articles/f2e8ecaf6bd8bf)を参考にしてください。
- 上記に加え、[@tauri-apps/cli](https://github.com/tauri-apps/tauri#readme) をグローバルインストール済みであること。

</details>


## Nx ワークスペースの作成ステップ

### 1. ディレクトリ構造

```batch
my-tauri-app/      (Nx ワークスペース)  
└── apps/
    ├── frontend/  (Angularプロジェクト)
    └── desktop/   (Rust + Tauriプロジェクト)
```

### 2. Nx コマンドでワークスペース作成と Angular app を追加

- Nx CLIをインストールして、プロジェクトを作成

  ```batch
  $ npx create-nx-workspace@latest my-tauri-app --preset=angular --appName=frontend
  ```

<details>
<summary>設定情報</summary>

![スクリーンショット 2024-12-08 16.00.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/2c7a7cc8-918c-d369-6ba3-c5393934487d.png)

</details>

<details>
<summary>frontend の動作確認</summary>

- 下記コマンドを実行して、この画面が表示されれば、確認完了です。
  ```batch
  $ npx nx serve frontend
  ```
  
  ![スクリーンショット 2024-12-08 22.09.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/e5f88c3a-3165-b882-8233-1624bfdae928.png)

</details>

#### [2 のサンプルコード](https://github.com/gurezo/my-tauri-app/pull/1)


### 3. Tauriプロジェクトの追加

<details>
<summary>Tauri プロジェクト作成</summary>

#### Tauri プロジェクト用ディレクトリの作成、移動

  
```batch
$ mkdir desktop
$ cd desktop
```

#### tauri cli コマンドで Tauri プロジェクトの初期化
  
```batch
$ npx tauri init

# アプリケーション名
✔ What is your app name? · tauri desktop app

# ウィンドウタイトル部の名前
✔ What should the window title be? · tarui desktop app 

# Web アプリ（Angular）の assets 
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../../dist/apps/frontend

# Web アプリ（Angular）の サーバー指定
✔ What is the url of your dev server? · http://localhost:4200/

# Web アプリ（Angular）の サーバー起動コマンド
✔ What is your frontend dev command? · npx nx serve frontend

# Web アプリ（Angular）の ビルドコマンド
✔ What is your frontend build command? · npx nx build frontend
```

- `apps/desktop/src-tauri` に tauri.conf.json が作成されました。
- Angular と Tauri(Rust) の連携する為の設定項目です。


</details>


#### tauri.conf.json 設定の解説

<details>
<summary>build セクション</summary>

```json
"build": {
  "frontendDist": "../../../dist/apps/frontend",
  "devUrl": "http://localhost:4200/",
  "beforeDevCommand": "npx nx serve frontend",
  "beforeBuildCommand": "npx nx build frontend"
}
```

- frontendDist
  - 概要: フロントエンドのビルド成果物（静的ファイル）が配置されるディレクトリを指定します。
  - 用途: Tauri はこのディレクトリ内の index.html をアプリケーションのエントリポイントとして使用します。
  - 今回の場合、Angular のビルド成果物が Nx ワークスペース内の dist/apps/frontend に生成される場合をを想定しています。
- devUrl
  - 概要: 開発モードでアプリを実行する際に使用する URL を指定します。
  - 用途: 通常はフロントエンド開発サーバーの URLを指定します。
  - 今回の場合、Angular のデフォルト開発サーバーが http://localhost:4200 で動作する事を想定しています。
- beforeDevCommand
  - 概要: 開発モードを開始する前に実行するコマンドを指定します。
  - 用途: フロントエンド開発サーバーを起動するコマンドを指定するのが一般的です。
  - 今回の場合、Angular アプリケーションを npx nx serve コマンドを想定しています。
- beforeBuildCommand
  - 概要: アプリをビルドする前に実行するコマンドを指定します。
  - 用途: フロントエンドのビルドプロセスを実行するコマンドを指定します。
  - 今回の場合、Angular アプリケーションを npx nx build コマンドを想定しています。

</details>


<details>
<summary>app セクション</summary>
Tauri アプリケーションのメタ情報や動作設定を行います。


```json
  "app": {
    "windows": [
      {
        "title": "tarui desktop app",
        .....
      }
    ],
    .....
  },
```

- windows
  - 概要: アプリケーションウィンドウの設定を指定します。
  - 用途: 初期状態のウィンドウサイズやタイトルなど、ウィンドウ関連の設定をカスタマイズします。
  - title
    - 概要: アプリケーションウィンドウのタイトルを設定します。
    - 用途: ユーザーがアプリを開いたときに表示されるウィンドウタイトルを指定します。
</details>

<details>
<summary>identifier セクション</summary>

※ Tauri のビルドの時の注意事項と対処
この状態で、`npx tauri build` を実行すると下記エラーになります。
`identifier は、初期化後に修正を実施します。`


```batch
$ npx tauri build
  Error You must change the bundle identifier in `tauri.conf.json identifier`. The default value `com.tauri.dev` is not allowed as it must be unique across applications.
```
- エラーメッセージの通り、tauri.conf.json の identifier を任意の値に変更する必要があります。
- 下記のように修正します。

```diff
....
  "$schema": "https://schema.tauri.app/config/2.0.0-rc",
  "productName": "tauri desktop app",
  "version": "0.1.0",
-  "identifier": "com.tauri.dev",
+  "identifier": "tauri.desktop.app",
....
````

</details>


<details>
<summary>tauri.conf.json 実装例</summary>

```json
{
  "$schema": "https://schema.tauri.app/config/2.0.0-rc",
  "productName": "tauri desktop app", 
  "version": "0.1.0",
  "identifier": "tauri.desktop.app",
  "build": {
    "frontendDist": "../../../dist/apps/frontend",
    "devUrl": "http://localhost:4200/",
    "beforeDevCommand": "npx nx serve frontend",
    "beforeBuildCommand": "npx nx build frontend"
  },
  "app": {
    "windows": [
      {
        "title": "tarui desktop app",
        "width": 800,
        "height": 600,
        "resizable": true,
        "fullscreen": false
      }
    ],
    "security": {
      "csp": null
    }
  },
  "bundle": {
    "active": true,
    "targets": "all",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  }
}
```
</details>

</details>

</details>

#### Tauri プロジェクトの動作確認

<details>
<summary>ターミナルを 2 つ起動</summary>

![スクリーンショット 2024-12-08 23.02.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/3cc6a85a-e544-2af9-7eef-be73ee6213ac.png)


##### Angular アプリを先に起動します。

```batch
$ npx nx serve frontend
```

##### Tauri プロジェクトをビルドします。

```batch
$ npx tauri build
```

##### Tauri プロジェクトビルド成功時

tauri.conf.json の identifier で定義した tauri.desktop.app が作成されます

![スクリーンショット 2024-12-08 23.04.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/d1486743-7695-8d2c-71b5-fa0c736d58f9.png)

</details>

#### Tauri のデスクトップアプリ起動

<details>
<summary>ビルド結果の app をアプリケーションフォルダへ移動（macOS の場合）</summary>

![スクリーンショット 2024-12-08 23.05.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/31fe8d61-69e6-138d-f12f-e9e732bd5bea.png)

</details>

<details>
<summary>tauri.desktop.app をダブルクリック</summary>

![スクリーンショット 2024-12-08 23.05.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/38a27e19-3de5-3e00-f474-dc26d480feb3.png)

</details>

#### [3 のサンプルコード](https://github.com/gurezo/my-tauri-app/pull/2)


## デスクトップアプリをカスタマイズしてみよう

### テーマ：入力された値が 2 倍に計算され、結果が表示される実装

#### tauri コマンド実行用 npm パッケージ追加
- add @tauri-apps/api [97af060](https://github.com/gurezo/my-tauri-app/commit/97af06084cbb801cee2510030a19c0360b043be3)

<details>

- @tauri-apps/api を追加 

```sh
$ npm install @tauri-apps/api --save-dev
```
- tsconfig.base.json に @tauri-apps/api を追加

```diff
  {
    "compileOnSave": false,
    "compilerOptions": {
      ......
      "baseUrl": ".",
-     "paths": {}
+     "paths": {},
+     "types": ["@tauri-apps/api"]
    },
    "exclude": ["node_modules", "tmp"]
  }
```

</details>


#### Tauri(Rust) 側実装変更

- update code tauri / rust [d81bceb](https://github.com/gurezo/my-tauri-app/commit/d81bceb3cc314c4d4f07825269a113d94536aea1)


<details>

- lib.rs 下記のように編集

```diff
  #[cfg_attr(mobile, tauri::mobile_entry_point)]
  pub fn run() {
  tauri::Builder::default()
+     .invoke_handler(tauri::generate_handler![double_value]) // tauri コマンドを追加
      .setup(|app| {
        if cfg!(debug_assertions) {
          app.handle().plugin(
            tauri_plugin_log::Builder::default()
              .level(log::LevelFilter::Info)
              .build(),
          )?;
        }
        Ok(())
      })
      .run(tauri::generate_context!())
      .expect("error while running tauri application");
  }
+ 
+ // 入力値を2倍にする関数
+ #[tauri::command]
+ fn double_value(value: i32) -> i32 {
+   value * 2
+ }
```

- tauri.conf.json の画面サイズを変更

```diff
  "app": {
    "windows": [
      {
        "title": "tarui desktop app",
-        "width": 800,
+        "width": 400,
-       "height": 600,
+       "height": 300,
        "resizable": true,
        "fullscreen": false
      }
    ],
    "security": {
      "csp": null
    }
  },
```

</details>

#### Angular 側実装変更

- update code angular [20d041b](https://github.com/gurezo/my-tauri-app/commit/20d041bcc10c241c7362fbc62c124959e178af0f)

<details>

nx ワークスペース作成と同時に Angular アプリケーションを作成した際に
apps/frontend/src/app/nx-welcome.component.ts が作成されています。
今回は nx-welcome.component.ts に UI や tauri 連携処理を実装します。

- apps/frontend/src/app/app.component.ts から不要コード削除、参照ライブラリ修正

```diff
  import { Component } from '@angular/core';
- import { RouterModule } from '@angular/router';
+ import { RouterOutlet } from '@angular/router';
  import { NxWelcomeComponent } from './nx-welcome.component';

  @Component({
-   imports: [NxWelcomeComponent, RouterModule],
+   imports: [NxWelcomeComponent, RouterOutlet],
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrl: './app.component.scss',
  })
- export class AppComponent {
-   title = 'frontend';
- }
+ export class AppComponent {}
```


- apps/frontend/src/app/nx-welcome.component.ts に 計算用 UI 追加、tauri コマンド連携実装

```diff
- import { Component, ViewEncapsulation } from '@angular/core';
+ import { Component } from '@angular/core';
- import { CommonModule } from '@angular/common';
+ import { FormsModule } from '@angular/forms';
+ import { invoke } from '@tauri-apps/api/core';

  @Component({
    selector: 'app-nx-welcome',
-   imports: [CommonModule],
+   standalone: true,
+   imports: [FormsModule],
    template: `
-     <!--
-       * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-    .......
+   <div>
+     <h1>Double Your Number</h1>
+     <input
+       [(ngModel)]="inputValue"
+       type="number"
+       placeholder="Enter a number"
+     />
+     <button (click)="doubleValue()">Double</button>
+     @if (result !== null) {
+       <p>Result: {{ result }}</p>
+     } @else {
+       <p>before calculation</p>
+     }
+   </div>
  `,
    styles: [],
-   encapsulation: ViewEncapsulation.None,
  })
-  export class NxWelcomeComponent {}
+  export class NxWelcomeComponent {
+     inputValue = 0; // ユーザーの入力値
+     result: number | null = null; // 計算結果
+
+    async doubleValue() {
+      // Rust 側の `double_value` コマンドを呼び出す
+      this.result = await invoke<number>('double_value', {
+        value: this.inputValue,
+      });
+    }
+  }
```



</details>

### 動作確認動画

[実装変更のサンプルコード](https://github.com/gurezo/my-tauri-app/pull/3)

![tarui-act-check.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28875/6f0af36d-e21d-7f8c-ed2a-a351b771ba31.gif)

ビルドから動作確認を全て見たい方は、こちらから[動画（102MB）](https://www.dropbox.com/scl/fi/8bgajs3ir8c7vynbf1kmz/2024-12-11-22.16.37.mov?rlkey=4g3tf5xbbe9nlf9nucctwyyt5&st=x1roro2x&dl=0)を御覧ください

今回紹介した記事のコードは、下記リポジトリです。

https://github.com/gurezo/my-tauri-app

### まとめ  

- 本記事では、Web 開発の知識を活用しつつ、新しい技術（Rust や Tauri ）に触れることで、効率的かつ柔軟なデスクトップアプリ開発の方法を解説しました。  
- 技術ごとの役割を理解し、フロントエンドとバックエンドを組み合わせたモダンな開発環境を構築できます。  

ぜひ、この記事を参考にデスクトップアプリ開発に挑戦してみてください！  
読者の皆さまのプロジェクトが成功することを願っています。  

明日 12/16(月) は、我らが黄色い人！！！
[@fusho-takahashi さん](https://qiita.com/fusho-takahashi)です！
よろしくお願い申し上げます！