i18n(ja): Add Japanese Texts to Develop secton (part 1) (#3268)

Co-authored-by: Vitor Ayres <gitkey@virtuaires.com.br>
Co-authored-by: Vitor Ayres <gitme@virtuaires.com.br>
This commit is contained in:
Junichi TAKAI (たかい じゅんいち)
2025-08-20 07:30:31 +09:00
committed by GitHub
parent aab78609bf
commit 2310ec1632
11 changed files with 2350 additions and 3 deletions

View File

@@ -0,0 +1,147 @@
---
title: Rust からフロントエンドを呼び出す
sidebar:
order: 1
---
`@tauri-apps/api` NPM パッケージは、グローバル・イベントと Webview 固有のイベントの両方を検知(リッスン)するための API を提供しています。
- グローバル・イベントの検知
```ts
import { listen } from '@tauri-apps/api/event';
type DownloadStarted = {
url: string;
downloadId: number;
contentLength: number;
};
listen<DownloadStarted>('download-started', (event) => {
console.log(
`downloading ${event.payload.contentLength} bytes from ${event.payload.url}`
);
});
```
- Webview 固有イベントの検知
```ts
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
const appWebview = getCurrentWebviewWindow();
appWebview.listen<string>('logged-in', (event) => {
localStorage.setItem('session-token', event.payload);
});
```
The `listen` 関数は、アプリケーションの全「[ライフタイム](https://doc.rust-lang.org/rust-by-example/ja/scope/lifetime.html)」期間中、イベント・リスナーの登録は維持されたままです。
イベントの検知(リッスン)を停止するには、`listen` 関数によって返される `unlisten` 関数を使用できます:
```js
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('download-started', (event) => {});
unlisten();
```
:::note
コンポーネントがアンマウントされるときなど、実行コンテキストがスコープ(有効範囲)外になる場合は、常に `unlisten` 関数を使用してください。
ページが再読み込みされるか、別の URL に移動すると、リスナーは自動的に登録解除されます。
ただし、この仕組みは「シングル・ページ・アプリケーションSPA」ルーターには適用されません。
> > > 《訳注》 **シングル・ページ・アプリケーション** 一つのページでコンテンツの切り替えを行なうアプリケーションや Web ページの構造。詳しくは [Wikipedia](https://ja.wikipedia.org/wiki/シングルページアプリケーション) を参照してください。
> > > :::
さらに、Tauri ではイベントを一度だけ検知(リッスン)するためのユーティリティ関数を提供しています:
```js
import { once } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
once('ready', (event) => {});
const appWebview = getCurrentWebviewWindow();
appWebview.once('ready', () => {});
```
:::note
フロントエンドで発行されたイベントは、上記の API によって登録されたリスナーもトリガーします。
詳細については、次章 [フロントエンドから Rust を呼び出す] の説明を参照してください。
:::
#### Rust のイベントを検知
グローバル・イベントも Webview 固有のイベントも、Rust に登録されたリスナーに配信されます。
- グローバル・イベントの検知
```rust title="src-tauri/src/lib.rs"
use tauri::Listener;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
app.listen("download-started", |event| {
if let Ok(payload) = serde_json::from_str::<DownloadStarted>(&event.payload()) {
println!("downloading {}", payload.url);
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
- Webview 固有イベントの検知
```rust title="src-tauri/src/lib.rs"
use tauri::{Listener, Manager};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview_window("main").unwrap();
webview.listen("logged-in", |event| {
let session_token = event.data;
// save token..
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
The `listen` 関数は、アプリケーションの全「ライフタイム」期間中、イベント・リスナーの登録は維持されたままです。
イベントの検知(リッスン)を停止するには、`listen` 関数によって返される `unlisten` 関数を使用できます:
```rust
// unlisten outside of the event handler scope:
let event_id = app.listen("download-started", |event| {});
app.unlisten(event_id);
// unlisten when some event criteria is matched
let handle = app.handle().clone();
app.listen("status-changed", |event| {
if event.data == "ready" {
handle.unlisten(event.id);
}
});
```
さらに、Tauri はイベントを一度だけ検知(リッスン)するためのユーティリティ関数を提供しています:
```rust
app.once("ready", |event| {
println!("app is ready");
});
```
この場合、イベント・リスナーは最初のトリガー後にすぐに登録が解除されます。
[フロントエンドから Rust を呼び出す]: /ja/develop/calling-rust/

View File

@@ -0,0 +1,274 @@
---
title: Rust からフロントエンドを呼び出す
sidebar:
order: 1
i18nReady: true
---
import { Content as FrontendListen } from './_sections/frontend-listen.mdx';
この文書には、Rust コードからアプリケーションのフロントエンドと通信する方法についての手引が書かれています。
フロントエンドから Rust コードと通信する方法については、次節の「[フロントエンドから Rust を呼び出す]」を参照してください。
Tauri アプリケーションの Rust 側では、「Tauri イベント・システム」を利用したり、「チャネル」を使用したり、あるいは JavaScript コードを直接検証することで、フロントエンドを呼び出すことができます。
## イベント・システム
Tauri には、Rust とフロントエンド間の双方向通信が行なえるシンプルな「イベント・システム」が付属しています。
このイベント・システムは、少量のデータをストリーミングする必要がある場合や、複数コンシューマー/複数プロデューサーのパターン(たとえば、プッシュ通知システムなど)を実装する必要があるといった状況を考慮して設計されています。
低遅延(レイテンシー)あるいは高スループットの状況向けの設計ではありません。ストリーミング・データ向けに最適化された実装については、[チャンネル](#チャネル-channels) の項を参照してください。
「Tauri コマンド」と「Tauri イベント」の大きな違いは、イベントには強力な型のサポートがなく、イベント・ペイロード(データ本体)が常に JSON 文字列であるために大きなメッセージには適さず、イベント・データとチャンネルを細かく制御するための [セキュリティ・レベル] システムがサポートされていないことです。
構造体 [AppHandle] および [WebviewWindow] の型(タイプ)は、イベント・システム・トレイトの [Listener] および [Emitter] を実装します。
イベントは、「グローバル」すべての「リスナー」に配信されるまたは「Webview 固有」(指定されたラベルに一致する Webview にのみ配信される)のどちらかになります。
### グローバル・イベント
グローバル・イベントをトリガー(開始)するには、[Emitter#emit] 関数を使用します:
```rust title="src-tauri/src/lib.rs"
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn download(app: AppHandle, url: String) {
app.emit("download-started", &url).unwrap();
for progress in [1, 15, 50, 80, 100] {
app.emit("download-progress", progress).unwrap();
}
app.emit("download-finished", &url).unwrap();
}
```
:::note
グローバル・イベントは**すべての**リスナーに配信されます。
:::
### Webview イベント
特定の Webview により指定されたリスナーに対してイベントをトリガーするには、[Emitter#emit_to] 関数を使用します:
```rust title="src-tauri/src/lib.rs"
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn login(app: AppHandle, user: String, password: String) {
let authenticated = user == "tauri-apps" && password == "tauri";
let result = if authenticated { "loggedIn" } else { "invalidCredentials" };
app.emit_to("login", "login-result", result).unwrap();
}
```
[Emitter#emit_filter] を呼び出すことで、Webview のリストにイベントをトリガーすることもできます。
次の例では、「open-file」イベントを、メインおよびファイル・ビューアーの Webview に発行します:
```rust title="src-tauri/src/lib.rs"
use tauri::{AppHandle, Emitter, EventTarget};
#[tauri::command]
fn open_file(app: AppHandle, path: std::path::PathBuf) {
app.emit_filter("open-file", path, |target| match target {
EventTarget::WebviewWindow { label } => label == "main" || label == "file-viewer",
_ => false,
}).unwrap();
}
```
:::note
Webview 固有のイベントは、通常のグローバル・イベント・リスナーに対してはトリガー**されません**。
**どの** イベントも検知(リッスン)するためには、`listen` 関数の代わりに `listen_any` 関数を使用する必要があります。この関数は、発行されたイベントを「キャッチオール」(すべて捕捉)するリスナーを定義します。
:::
### イベント・ペイロード
イベント・ペイロードは、任意の [直列化可能 serializable][Serialize] 型にすることができ、この型は [Clone] トレイトも実装できます。
イベントごとに多くの情報を発行するオブジェクトを使用して、「ダウンロード・イベント例」を拡張してみましょう。
```rust title="src-tauri/src/lib.rs"
use tauri::{AppHandle, Emitter};
use serde::Serialize;
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadStarted<'a> {
url: &'a str,
download_id: usize,
content_length: usize,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadProgress {
download_id: usize,
chunk_length: usize,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadFinished {
download_id: usize,
}
#[tauri::command]
fn download(app: AppHandle, url: String) {
let content_length = 1000;
let download_id = 1;
app.emit("download-started", DownloadStarted {
url: &url,
download_id,
content_length
}).unwrap();
for chunk_length in [15, 150, 35, 500, 300] {
app.emit("download-progress", DownloadProgress {
download_id,
chunk_length,
}).unwrap();
}
app.emit("download-finished", DownloadFinished { download_id }).unwrap();
}
```
### イベントの検知(リスニング)
Tauri は、Webview と Rust 双方のインターフェースでイベントを検知(リッスン)するための API を提供しています。
#### フロントエンドのイベントを検知
<FrontendListen />
## チャネル Channels
> > > 《訳注》 **チャネル** 「流路、伝送路」。日本語では一般に「チャンネル」と表記されますが、コンピュータ用語としては「チャネル」表記が一般的であるので、その表記に従って記述します。
イベント・システムは、アプリケーションで「グローバル」に利用できるシンプルな双方向通信を行なうように設計されています。
内部的には直接 JavaScript コードを検証するため、大量のデータを送信するには適さない可能性があります。
チャネルは高速に、順序付けられたデータを配信するように設計されています。したがって、ダウンロードの進行状況、子プロセスの出力、WebSocket メッセージなどのストリーミング操作のために内部的に使用されます。
上述の「ダウンロード・コマンドの事例」を、「イベント・システム」の代わりに「チャネル」を使用するように書き直してみましょう:
```rust title="src-tauri/src/lib.rs"
use tauri::{AppHandle, ipc::Channel};
use serde::Serialize;
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
enum DownloadEvent<'a> {
#[serde(rename_all = "camelCase")]
Started {
url: &'a str,
download_id: usize,
content_length: usize,
},
#[serde(rename_all = "camelCase")]
Progress {
download_id: usize,
chunk_length: usize,
},
#[serde(rename_all = "camelCase")]
Finished {
download_id: usize,
},
}
#[tauri::command]
fn download(app: AppHandle, url: String, on_event: Channel<DownloadEvent>) {
let content_length = 1000;
let download_id = 1;
on_event.send(DownloadEvent::Started {
url: &url,
download_id,
content_length,
}).unwrap();
for chunk_length in [15, 150, 35, 500, 300] {
on_event.send(DownloadEvent::Progress {
download_id,
chunk_length,
}).unwrap();
}
on_event.send(DownloadEvent::Finished { download_id }).unwrap();
}
```
ダウンロード・コマンドを呼び出すときには、チャネルを作成し、それを引数として指定する必要があります:
```ts
import { invoke, Channel } from '@tauri-apps/api/core';
type DownloadEvent =
| {
event: 'started';
data: {
url: string;
downloadId: number;
contentLength: number;
};
}
| {
event: 'progress';
data: {
downloadId: number;
chunkLength: number;
};
}
| {
event: 'finished';
data: {
downloadId: number;
};
};
const onEvent = new Channel<DownloadEvent>();
onEvent.onmessage = (message) => {
console.log(`got download event ${message.event}`);
};
await invoke('download', {
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-schema-generator/schemas/config.schema.json',
onEvent,
});
```
## JavaScript の検証
Webview [コンテキスト](https://ja.wikipedia.org/wiki/コンテクスト) 上で JavaScript コードを直接実行するには、[`WebviewWindow#eval`] 関数を使用できます:
```rust title="src-tauri/src/lib.rs"
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview_window("main").unwrap();
webview.eval("console.log('hello from Rust')")?;
Ok(())
})
```
検証されるスクリプトがそれほど単純ではなく、Rust オブジェクトからの入力を使用しなければならない場合は、[serialize-to-javascript] クレートの使用をお勧めします。
[`WebviewWindow#eval`]: https://docs.rs/tauri/2.0.0/tauri/webview/struct.WebviewWindow.html#method.eval
[serialize-to-javascript]: https://docs.rs/serialize-to-javascript/latest/serialize_to_javascript/
[AppHandle]: https://docs.rs/tauri/2.0.0/tauri/struct.AppHandle.html
[WebviewWindow]: https://docs.rs/tauri/2.0.0/tauri/webview/struct.WebviewWindow.html
[Listener]: https://docs.rs/tauri/2.0.0/tauri/trait.Listener.html
[Emitter]: https://docs.rs/tauri/2.0.0/tauri/trait.Emitter.html
[Emitter#emit]: https://docs.rs/tauri/2.0.0/tauri/trait.Emitter.html#tymethod.emit
[Emitter#emit_to]: https://docs.rs/tauri/2.0.0/tauri/trait.Emitter.html#tymethod.emit_to
[Emitter#emit_filter]: https://docs.rs/tauri/2.0.0/tauri/trait.Emitter.html#tymethod.emit_filter
[Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html
[Serialize]: https://serde.rs/impl-serialize.html
[フロントエンドから Rust を呼び出す]: /ja/develop/calling-rust/
[セキュリティ・レベル]: /ja/security/capabilities/
<div style="text-align: right">
【※ この日本語版は、「Feb 22, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,705 @@
---
title: フロントエンドから Rust を呼び出す
sidebar:
order: 1
i18nReady: true
---
import { Content as FrontendListen } from './_sections/frontend-listen.mdx';
この文書には、アプリケーションのフロントエンドから Rust コードと通信する方法についての手引が書かれています。
Rust コードからフロントエンドと通信する方法については、前節の[Rust からフロントエンドを呼び出す] を参照してください。
Tauri は、より動的な [イベント・システム](#イベントシステム) と共に、型安全性を備えた Rust 関数にアクセスするための [コマンド](#コマンド)・プリミティブを提供します。
## コマンド
Tauri は、Web アプリから Rust 関数を呼び出すためのシンプルでありながら強力な「コマンド `command`」システムを提供しています。
「コマンド」は引数を受け入れ、値を返すことができます。また、エラーを返したり、`async`(非同期)にしたりもできます。
### 基本的な例
コマンドは `src-tauri/src/lib.rs` ファイルで定義できます。
コマンドを作成するには、関数を追加し、`#[tauri::command]` で注釈を付けるだけです:
```rust title="src-tauri/src/lib.rs"
#[tauri::command]
fn my_custom_command() {
println!("I was invoked from JavaScript!");
}
```
:::note
コマンド名は一意でなければなりません。
:::
:::note
`lib.rs` ファイルに定義されたコマンドは、「[グルーコード](https://ja.wikipedia.org/wiki/グルーコード)生成」に対する制約のため、`pub`(パブリック/公開)として指定できません。
「パブリック関数」として指定すると、次のようなエラーが表示されます:
````
error[E0255]: the name `__cmd__command_name` is defined multiple times 《`__cmd__command_name`という名称は複数定義されています》
--> src/lib.rs:28:8
|
27 | #[tauri::command]
| ----------------- previous definition of the macro `__cmd__command_name` here 《マクロ `__cmd__command_name` の以前の定義はこちら》
28 | pub fn x() {}
| ^ `__cmd__command_name` reimported here 《`__cmd__command_name` はここに再インポートされます》
|
= note: `__cmd__command_name` must be defined only once in the macro namespace of this module 《注記 `__cmd__command_name` はこのモジュールのマクロ名前空間に一度だけ定義できます》
```
:::
コマンドのリストは、次のように、「ビルダー」関数に提供する必要があります。
```rust title="src-tauri/src/lib.rs" ins={4}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![my_custom_command])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
````
これで、JavaScript コードからコマンドを呼び出すことができます。
```javascript
// Tauri API の npm パッケージを用いる場合:
import { invoke } from '@tauri-apps/api/core';
// Tauri global script を用いる場合npm パッケージを使わない時)
// `tauri.conf.json` の `app.withGlobalTauri` を必ず true に設定してください。
const invoke = window.__TAURI__.core.invoke;
// コマンドを呼び出す
invoke('my_custom_command');
```
#### 別モジュールにコマンドを定義する
アプリケーションで多数のコンポーネントを定義している場合、あるいはコンポーネントがグループ化できる場合、`lib.rs` ファイルにコマンド定義を詰め込むのではなく、別のモジュールにコマンドを定義できます。
例として、`src-tauri/src/commands.rs` ファイルにコマンドを定義してみましょう。
```rust title="src-tauri/src/commands.rs"
#[tauri::command]
pub fn my_custom_command() {
println!("I was invoked from JavaScript!");
}
```
:::note
別モジュールにコマンドを定義する場合は、そのコマンドを `pub` として指定する必要があります。
:::
:::note
コマンド名はそのコマンドが定義されているモジュールに限定されていないため、モジュール間においても一意でなければなりません。
:::
`lib.rs` ファイルでは、モジュールを定義し、それに応じたコマンドのリストを提供します:
```rust title="src-tauri/src/lib.rs" ins={6}
mod commands;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![commands::my_custom_command])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
コマンド・リスト内の「プレフィックス `commands::`」に注意してください。これは、このコマンド関数への完全なパスを意味しています。
この例にあるコマンド名は `my_custom_command` なので、フロントエンド内で `invoke("my_custom_command")` を実行して呼び出すことができ、プレフィックスの `commands::` は無視されます。
#### WASMウェブアセンブリ
Rust フロントエンドを使用して、引数なしで `invoke()` を呼び出す場合は、フロントエンド・コードを以下のように変更する必要があります。
その理由は、Rust がオプションの引数をサポートしていないためです。
```rust ins={4-5}
#[wasm_bindgen]
extern "C" {
// 引数なして呼び出す
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], js_name = invoke)]
async fn invoke_without_args(cmd: &str) -> JsValue;
// 引数ありで呼び出す(デフォルト)
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
// この二つは別の名前にする必要があります!
}
```
### 引数を受け渡す
コマンド・ハンドラーは引数を取ることができます:
```rust
#[tauri::command]
fn my_custom_command(invoke_message: String) {
println!("I was invoked from JavaScript, with this message: {}", invoke_message);
}
```
引数は、camelCase キーを持つ JSON オブジェクトとして渡す必要があります。
> > > 《訳注》 英語複合語の表記法について:
> > > ・**camelCase** 「[キャメルケース](https://ja.wikipedia.org/wiki/キャメルケース)」 繋げた単語の初めの文字を大文字にする記法
> > > ・**snake_case** 「[スネークケース](https://ja.wikipedia.org/wiki/スネークケース)」 繋げる単語の間にアンダースコア(\_を入れて全て小文字で書く記法
```javascript
invoke('my_custom_command', { invokeMessage: 'Hello!' });
```
:::note
`rename_all` 属性を持つ引数には `snake_case` を使用できます:
```rust
#[tauri::command(rename_all = "snake_case")]
fn my_custom_command(invoke_message: String) {}
```
```javascript
invoke('my_custom_command', { invoke_message: 'Hello!' });
```
:::
引数は、[`serde::Deserialize`] を実装している限り、どのような型でも可能です。
対応する JavaScript:
```javascript
invoke('my_custom_command', { invoke_message: 'Hello!' });
```
### データを返す
コマンド・ハンドラーもデータを返します:
```rust
#[tauri::command]
fn my_custom_command() -> String {
"Hello from Rust!".into()
}
```
`invoke` 関数「呼び出し」関数は、戻り値によって処理内容が決まる「promise」を返します
```javascript
invoke('my_custom_command').then((message) => console.log(message));
```
返されるデータは、[`serde::Serialize`] を実装している限り、どの型でも構いません。
#### 配列バッファを返す
[`serde::Serialize`] を実装する戻り値は、応答がフロントエンドに送信されると JSON 形式にシリアル化されます。
この方法では、もしファイルやダウンロード HTTP 応答などの大きなデータを返そうとすると、アプリケーションの速度が低下する可能性があります。
配列バッファを最適化された方法で返すには、[`tauri::ipc::Response`] を使用します。
> > > 《訳注》 **シリアル化** serialize 複数の並列データを「直列化」一列に並べる操作して、ファイル保存やネットワーク送受信できるように変換する処理。
```rust
use tauri::ipc::Response;
#[tauri::command]
fn read_file() -> Response {
let data = std::fs::read("/path/to/file").unwrap();
tauri::ipc::Response::new(data)
}
```
### エラー処理
もしハンドラーが処理に失敗しエラーを返す必要がある場合は、コマンド関数に `Result` を返させます。
```rust
#[tauri::command]
fn login(user: String, password: String) -> Result<String, String> {
if user == "tauri" && password == "tauri" {
// 処理成功
Ok("logged_in".to_string())
} else {
// 処理失敗
Err("invalid credentials".to_string())
}
}
```
コマンドがエラーを返す場合、「promise」は「処理失敗」、そうでない場合には「処理成功」となります
```javascript
invoke('login', { user: 'tauri', password: '0j4rijw8=' })
.then((message) => console.log(message))
.catch((error) => console.error(error));
```
上述のように、エラーも含めて、コマンドから返されるものはすべて [`serde::Serialize`] を実装していなければなりません。
ただし、もし Rust の 標準ライブラリまたは外部クレートからのエラー・タイプを使用している場合、そのほとんどのエラー・タイプは [`serde::Serialize`] を実装していないため、問題が発生する可能性があります。
この問題を解決する単純なシナリオは、`map_err` を使用してこれらのエラーを `String` に変換することです。
```rust
#[tauri::command]
fn my_custom_command() -> Result<(), String> {
std::fs::File::open("path/to/file").map_err(|err| err.to_string())?;
// 成功すると `null` を返します
Ok(())
}
```
この解決法はあまり一般的ではないので、`serde::Serialize` を実装する独自のエラー・タイプを作成することをお勧めします。
次の例では、[`thiserror`] クレートを使用してエラー・タイプを作成しています。
これにより、`thiserror::Error` トレイトを派生させることで、列挙型enumをエラー型に変換できます。
詳細については、[関連文書(英語版)](https://serde.rs/)を参照してください。
```rust
// プログラムで起こり得るすべてのエラーを表すエラー・タイプを作成する
#[derive(Debug, thiserror::Error)]
enum Error {
#[error(transparent)]
Io(#[from] std::io::Error)
}
// 「serde::Serialize」を手動で実装する必要があります
impl serde::Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
#[tauri::command]
fn my_custom_command() -> Result<(), Error> {
// これでエラーが返されます
std::fs::File::open("path/that/does/not/exist")?;
// 成功すると `null` を返します
Ok(())
}
```
カスタムのエラー・タイプでは、起こり得るすべてのエラーを明示的に示すという利点があり、どのようなエラーが起こる可能性があるのかをすぐに識別できます。
これにより、後でコードのレビューやリファクタリングを行なうときに、他の人(および自分自身)の時間を大幅に節約できます。<br/>
また、エラー・タイプがシリアル化される方法を完全に管理することもできます。
上記の例では、エラー・メッセージを文字列として返すだけでしたが、それぞれのエラーにコードを割り当てて、よく似た見た目の「TypeScript エラー列挙型」に簡単にマップすることができます。たとえば、次のようになります:
```rust
#[derive(Debug, thiserror::Error)]
enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("failed to parse as string: {0}")]
Utf8(#[from] std::str::Utf8Error),
}
#[derive(serde::Serialize)]
#[serde(tag = "kind", content = "message")]
#[serde(rename_all = "camelCase")]
enum ErrorKind {
Io(String),
Utf8(String),
}
impl serde::Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let error_message = self.to_string();
let error_kind = match self {
Self::Io(_) => ErrorKind::Io(error_message),
Self::Utf8(_) => ErrorKind::Utf8(error_message),
};
error_kind.serialize(serializer)
}
}
#[tauri::command]
fn read() -> Result<Vec<u8>, Error> {
let data = std::fs::read("/path/to/file")?;
Ok(data)
}
```
あなたのフロントエンドは、これで、 `{ kind: 'io' | 'utf8', message: string }` エラー・オブジェクトが表示されるはずです:
```ts
type ErrorKind = {
kind: 'io' | 'utf8';
message: string;
};
invoke('read').catch((e: ErrorKind) => {});
```
### 非同期コマンド
Tauri では、UI のフリーズや速度の低下を起こさせないために、負荷の高い作業の実行には、「非同期コマンド」が推奨されています。
:::note
非同期コマンドでは、[`async_runtime::spawn`] を使用して、別の非同期タスク上で実行されます。
キーワード「_async_」の付与されていないコマンドは、_#[tauri::command(async)]_ で定義されていない限り、メインスレッドで実行されます。
:::
**コマンドを非同期で実行する必要がある場合は、そのコマンドを `async` とだけ宣言すれば大丈夫です。**
:::caution
Tauri を使用して非同期関数を作成するときには、注意が必要です。
現在、「借用した引数」(引き継がれた引数)を非同期関数の[シグネチャ](https://ja.wikipedia.org/wiki/シグネチャ)にそのまま含めることはできません。
このような型の一般的な例としては、`&str` や `State<'_, Data>` などがあります。
この制約は https://github.com/tauri-apps/tauri/issues/2533 (英語版)で議論されており、回避策は以下に示されているとおりです。
:::
「借用した型」を使用する場合は、追加の変更を加える必要があります。主なオプションは次の二つです:
**オプション 1** `&str` などの型を、`String` などの借用されていない類似の型に変換します。
この方法はすべての型で機能するとは限りません、たとえば `State<'_, Data>` など、では。
_例_
```rust
// 「&str」の代わりに「String」を使用して非同期関数を宣言します「&str」は借用されておりサポートされていません
#[tauri::command]
async fn my_custom_command(value: String) -> String {
// 別の非同期関数を呼び出し、その処理が完了するまで待機します
some_async_function().await;
value
}
```
**オプション 2**: 戻り値の型を [`Result`] でラップします。こちらの方法は実装が少し難しいですが、すべての型で機能します。
戻り値の型 `Result<a, b>` を使用し、`a` を「返したい型」で置き換えます。`null` を返したい場合は `()` に置き換えます。また、`b` を、何か問題が発生した場合に返す「エラー型」に置き換えます。オプションのエラーを返さないようにしたい場合は `()` に置き換えます。たとえば:
- `Result<String, ()>` 「String 型」を返し、エラーは返しません。
- `Result<(), ()>` 「`null`」を返します。
- `Result<bool, Error>` 上記の [エラー処理](#エラー処理) の項にあるように、「ブール値」または「エラー」を返します。
_例_
```rust
// 型借用の問題を回避するために、「Result<String, ()>」を返します
#[tauri::command]
async fn my_custom_command(value: &str) -> Result<String, ()> {
// 別の非同期関数を呼び出し、その処理が完了するまで待機します
some_async_function().await;
// 戻り値は「`Ok()`」でラップする必要があることに注意してください
Ok(format!(value))
}
```
##### JavaScript から呼び出す
JavaScript からのコマンド呼び出しでは「promise」が返されていますので、他のコマンドと同じように動作します
```javascript
invoke('my_custom_command', { value: 'Hello, Async!' }).then(() =>
console.log('Completed!')
);
```
### チャネル
「Tauri チャネル」は、フロントエンドへのストリーミング HTTP 応答のようなストリーミング・データに対する推奨メカニズムです。
次の例では、ファイルを読み取り、フロントエンドに 4096 バイトのチャンク(データ分割単位)で進行状況を通知します:
```rust
use tokio::io::AsyncReadExt;
#[tauri::command]
async fn load_image(path: std::path::PathBuf, reader: tauri::ipc::Channel<&[u8]>) {
// 説明簡潔化のため、この例ではエラー処理は含まれていません
let mut file = tokio::fs::File::open(path).await.unwrap();
let mut chunk = vec![0; 4096];
loop {
let len = file.read(&mut chunk).await.unwrap();
if len == 0 {
// 「長さ=ゼロ」とは、「ファイルの終わり」を意味します
break;
}
reader.send(&chunk).unwrap();
}
}
```
詳細については、「Rust からフロントエンドを呼び出す」の [チャネル] の説明を参照してください。
### コマンドで WebviewWindow にアクセスする
コマンドは、メッセージを呼び出した `WebviewWindow` のインスタンスにアクセスできます:
```rust title="src-tauri/src/lib.rs"
#[tauri::command]
async fn my_custom_command(webview_window: tauri::WebviewWindow) {
println!("WebviewWindow: {}", webview_window.label());
}
```
### コマンドで AppHandle にアクセスする
コマンドは、`AppHandle` のインスタンスにアクセスできます:
```rust title="src-tauri/src/lib.rs"
#[tauri::command]
async fn my_custom_command(app_handle: tauri::AppHandle) {
let app_dir = app_handle.path_resolver().app_dir();
use tauri::GlobalShortcutManager;
app_handle.global_shortcut_manager().register("CTRL + U", move || {});
}
```
### Managed State管理状態へのアクセス
Tauri は、`tauri::Builder` の `manage` 関数を使用して「状態 state」を管理できます。
「状態 state」には、`tauri::State` を使用したコマンドでアクセスできます:
```rust title="src-tauri/src/lib.rs"
struct MyState(String);
#[tauri::command]
fn my_custom_command(state: tauri::State<MyState>) {
assert_eq!(state.0 == "some state value", true);
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(MyState("some state value".into()))
.invoke_handler(tauri::generate_handler![my_custom_command])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
### Raw Request へのアクセス
Tauri コマンドでは、ボディ・ペイロード(ヘッダー部を除いた本体部分)の生データとリクエスト・ヘッダーを含む[`tauri::ipc::Request`] オブジェクト全体にもアクセスできます。
```rust
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("unexpected request body")]
RequestBodyMustBeRaw,
#[error("missing `{0}` header")]
MissingHeader(&'static str),
}
impl serde::Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
#[tauri::command]
fn upload(request: tauri::ipc::Request) -> Result<(), Error> {
let tauri::ipc::InvokeBody::Raw(upload_data) = request.body() else {
return Err(Error::RequestBodyMustBeRaw);
};
let Some(authorization_header) = request.headers().get("Authorization") else {
return Err(Error::MissingHeader("Authorization"));
};
// アップロード中 ...
Ok(())
}
```
フロントエンドでは、ペイロード引数に ArrayBuffer または Uint8Array を指定して Raw Request 本文を送信する「invoke()」を呼び出せます。三番目の引数にリクエスト・ヘッダーを含めることも可能です:
```js
const data = new Uint8Array([1, 2, 3]);
await __TAURI__.core.invoke('upload', data, {
headers: {
Authorization: 'apikey',
},
});
```
### 複数コマンドの作成
「`tauri::generate_handler!` マクロ」はコマンドの配列を受け取ります。複数のコマンドを登録する場合、invoke_handler を複数回呼び出すことはできません。最後の呼び出しのみが使用されます。それぞれのコマンドを一つずつ `tauri::generate_handler!` の呼び出しに渡す必要があります。
```rust title="src-tauri/src/lib.rs"
#[tauri::command]
fn cmd_a() -> String {
"Command a"
}
#[tauri::command]
fn cmd_b() -> String {
"Command b"
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![cmd_a, cmd_b])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
### 合体版プログラム例
上記の機能の一部または全部を組み合わせてることができます:
```rust title="src-tauri/src/lib.rs"
struct Database;
#[derive(serde::Serialize)]
struct CustomResponse {
message: String,
other_val: usize,
}
async fn some_other_function() -> Option<String> {
Some("response".into())
}
#[tauri::command]
async fn my_custom_command(
window: tauri::Window,
number: usize,
database: tauri::State<'_, Database>,
) -> Result<CustomResponse, String> {
println!("Called from {}", window.label());
let result: Option<String> = some_other_function().await;
if let Some(message) = result {
Ok(CustomResponse {
message,
other_val: 42 + number,
})
} else {
Err("No result".into())
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(Database {})
.invoke_handler(tauri::generate_handler![my_custom_command])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
```javascript
import { invoke } from '@tauri-apps/api/core';
// Invocation from JavaScript
invoke('my_custom_command', {
number: 42,
})
.then((res) =>
console.log(`Message: ${res.message}, Other Val: ${res.other_val}`)
)
.catch((e) => console.error(e));
```
## イベント・システム
「イベント・システム」は、フロントエンドと Rust 間のより簡潔な通信メカニズムです。
コマンドとは異なり、イベントは型安全ではなく、常に非同期であり、値を返すことができず、JSON ペイロードのみをサポートします。
### グローバル・イベント
グローバル・イベントをトリガーするには、[event.emit] または [WebviewWindow#emit] 関数が使用できます:
```js
import { emit } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
// emit(eventName, payload)
emit('file-selected', '/path/to/file');
const appWebview = getCurrentWebviewWindow();
appWebview.emit('route-changed', { url: window.location.href });
```
:::note
グローバル・イベントは **すべての** リスナーに配信されます。
:::
### Webview イベント
個別の Webview によって登録されたリスナーに対してイベントをトリガーするには、[event.emitTo] または [WebviewWindow#emitTo] 関数が使用できます:
```js
import { emitTo } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
// emitTo(webviewLabel, eventName, payload)
emitTo('settings', 'settings-update-requested', {
key: 'notification',
value: 'all',
});
const appWebview = getCurrentWebviewWindow();
appWebview.emitTo('editor', 'file-changed', {
path: '/path/to/file',
contents: 'file contents',
});
```
:::note
Webview 固有のイベントは、通常のグローバル・イベント・リスナーに対しては**トリガーされません**。
**あらゆる** イベントを検知(リッスン)するためには、[event.listen] 関数に `{ target: { kind: 'Any' } }` オプションを指定する必要があります。これは、リスナーが発行されたイベントをすべて捕捉する「キャッチオール」として機能するように定義するものです。
```js
import { listen } from '@tauri-apps/api/event';
listen(
'state-changed',
(event) => {
console.log('got state changed event', event);
},
{
target: { kind: 'Any' },
}
);
```
:::
### イベントの検知
<FrontendListen />
Rust コードからイベントを検知リッスンしたりイベントを発行したりする方法については、前章「Rust からフロントエンドを呼び出す」の [イベント・システムの説明] を参照してください。
[Rust からフロントエンドを呼び出す]: /ja/develop/calling-frontend/
[`async_runtime::spawn`]: https://docs.rs/tauri/2.0.0/tauri/async_runtime/fn.spawn.html
[`serde::serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
[`serde::deserialize`]: https://docs.serde.rs/serde/trait.Deserialize.html
[`tauri::ipc::Response`]: https://docs.rs/tauri/2.0.0/tauri/ipc/struct.Response.html
[`tauri::ipc::Request`]: https://docs.rs/tauri/2.0.0/tauri/ipc/struct.Request.html
[`thiserror`]: https://github.com/dtolnay/thiserror
[`result`]: https://doc.rust-lang.org/std/result/index.html
[event.emit]: /reference/javascript/api/namespaceevent/#emit
[event.listen]: /reference/javascript/api/namespaceevent/#listen
[WebviewWindow#emit]: /reference/javascript/api/namespacewebviewwindow/#emit
[event.emitTo]: /reference/javascript/api/namespaceevent/#emitto
[WebviewWindow#emitTo]: /reference/javascript/api/namespacewebviewwindow/#emitto
[イベント・システムの説明]: /ja/develop/calling-frontend/#イベントシステム
[チャネル]: /ja/develop/calling-frontend/#チャネル-channels
[フロントエンドから Rust を呼び出す]: /ja/develop/calling-rust/
<div style="text-align: right">
【※ この日本語版は、「Feb 22, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,286 @@
---
title: 設定ファイル
sidebar:
order: 1
i18nReady: true
---
import CommandTabs from '@components/CommandTabs.astro';
Tauri はアプリケーションを構築するためのツールキットであるため、プロジェクト設定を構成するためのファイルが多数になることもあります。よく目にする一般的なファイルは、`tauri.conf.json`、`package.json`、`Cargo.toml` などでしょう。この章では、どのファイルを修正すべきなのかを正しく判断できるように、それぞれについて簡単に説明します。
> > > 《訳注》 **設定**と**構成** どちらも configuration の訳語。本稿では厳密な区別はなく用いているため、訳文に違和感がある場合は適宜読み替えてください。
## Tauri の設定
「Tauri の設定」は、Web アプリ・ソースの定義、アプリケーションのメタデータの記述、バンドルの設定、プラグイン構成の設定、および、ウィンドウやトレイ・アイコン、メニューなどの設定による実行時の動作変更に使用されます。
このファイルは、「Tauri ランタイム」と「Tauri CLI」によって使用されています。「ビルド設定」を定義したり[`tauri build`][before-build-command] や [`tauri dev`][before-dev-command] が起動する前に実行されるコマンドなど)、アプリの [名前](/reference/config/#productname) や [バージョン](/reference/config/#version)を設定したり、[Tauri ランタイムの制御][appconfig]、[プラグインの設定] などが行なえます。
:::tip
すべてのオプションについては、[Reference の Configuration](「参考情報/設定」【英語版】)の項を参照してください。
:::
### サポートされている形式
デフォルトの Tauri 設定形式は「JSON」です。「JSON5」あるいは「TOML」形式も、`Cargo.toml` の `tauri` と `tauri-build` の依存関係に `config-json5` あるいは `config-toml` 機能フラグをそれぞれ追加することで有効化できます。
```toml title=Cargo.toml
[build-dependencies]
tauri-build = { version = "2.0.0", features = [ "config-json5" ] }
[dependencies]
tauri = { version = "2.0.0", features = [ "config-json5" ] }
```
体系と値はすべての形式で同じですが、書式設定はそれぞれのファイルの形式と一致している必要があります:
```json5 title=tauri.conf.json or tauri.conf.json5
{
build: {
devUrl: 'http://localhost:3000',
// start the dev server
beforeDevCommand: 'npm run dev',
},
bundle: {
active: true,
icon: ['icons/app.png'],
},
app: {
windows: [
{
title: 'MyApp',
},
],
},
plugins: {
updater: {
pubkey: 'updater pub key',
endpoints: ['https://my.app.updater/{{target}}/{{current_version}}'],
},
},
}
```
```toml title=Tauri.toml
[build]
dev-url = "http://localhost:3000"
# start the dev server
before-dev-command = "npm run dev"
[bundle]
active = true
icon = ["icons/app.png"]
[[app.windows]]
title = "MyApp"
[plugins.updater]
pubkey = "updater pub key"
endpoints = ["https://my.app.updater/{{target}}/{{current_version}}"]
```
JSON5 と TOML はコメントをサポートしており、TOML ではより慣用的な「ケバブ・ケース」を設定名に使用できることに注意してください。
> > > 《訳注》 **ケバブ・ケース** 原文 kebab-case。複合語を作成する場合に、小文字の単語間にハイフン-)入れて作成する命名規則。文字を串刺しにした形に見えるため「ケバブ・ケース」と呼ばれています。
### プラットフォーム固有の設定
デフォルトの設定ファイルに加えて、Tauri は以下の場所からプラットフォーム固有の設定を読み取ることができます:
- Linux 用: `tauri.linux.conf.json` または `Tauri.linux.toml`
- Windows 用: `tauri.windows.conf.json` または `Tauri.windows.toml`
- macOS 用: `tauri.macos.conf.json` または `Tauri.macos.toml`
- Android 用: `tauri.android.conf.json` または `Tauri.android.toml`
- iOS 用: `tauri.ios.conf.json` または `Tauri.ios.toml`
プラットフォーム固有の設定ファイルは、[JSON Merge Patch (RFC 7396)] 仕様に従ってメインの設定オブジェクトと統合(マージ)されます。
たとえば、次のような基本の `tauri.conf.json` があるとします:
```json title=tauri.conf.json
{
"productName": "MyApp",
"bundle": {
"resources": ["./resources"]
},
"plugins": {
"deep-link": {}
}
}
```
さらに、指定されたプラットフォーム固有の `tauri.linux.conf.json`
```json title=tauri.linux.conf.json
{
"productName": "my-app",
"bundle": {
"resources": ["./linux-assets"]
},
"plugins": {
"cli": {
"description": "My app",
"subcommands": {
"update": {}
}
},
"deep-link": {}
}
}
```
Linux 用の最終的な構成は次のオブジェクトのようになります:
> > > 《訳注》 **最終的な構成** 原文は resolved configuration。直訳「解決された設定構成」。「最終的に決定された」意味と解釈して訳してあります。次項「設定の拡張」での表現も同様。
```json
{
"productName": "my-app",
"bundle": {
"resources": ["./linux-assets"]
},
"plugins": {
"cli": {
"description": "My app",
"subcommands": {
"update": {}
}
},
"deep-link": {}
}
}
```
さらには、CLIコマンド・ライン・インターフェース経由でマージする設定を行なうこともできます。詳細については、次項を参照してください。
### 設定の拡張
Tauri CLI を使用すると、`dev`、`android dev`、`ios dev`、`build`、`android build`、`ios build`、または `bundle` コマンドのいずれかを実行するときに、Tauri の設定を拡張できます。
拡張設定は、JSON 文字列をそのままの形で、あるいは JSON ファイルへのパスとして、`--config` 引数により与えることが可能です。
Tauri は [JSON Merge Patch (RFC 7396)] 仕様を使用して、提供された設定値を元々の最終的な構成オブジェクトとマージします。
このメカニズムを使用すると、アプリケーションの複数のフレーバーを定義したり、アプリケーション・バンドルを構成する際の柔軟性を高めたりすることができます。
たとえば、正式版とは完全に切り離された*ベータ版*アプリケーションを配布するには、この機能を使用して別のアプリケーション名と識別子を設定できます:
```json title=src-tauri/tauri.beta.conf.json
{
"productName": "My App Beta",
"identifier": "com.myorg.myappbeta"
}
```
この切り離された*ベータ版*アプリを配布するには、ビルド時に以下の設定ファイルを作成します:
<CommandTabs
npm="npm run tauri build -- --config src-tauri/tauri.beta.conf.json"
yarn="yarn tauri build --config src-tauri/tauri.beta.conf.json"
pnpm="pnpm tauri build --config src-tauri/tauri.beta.conf.json"
deno="deno task tauri build --config src-tauri/tauri.beta.conf.json"
bun="bun tauri build --config src-tauri/tauri.beta.conf.json"
cargo="cargo tauri build --config src-tauri/tauri.beta.conf.json"
/>
## `Cargo.toml`
Cargo のマニフェスト・ファイルは、アプリが依存する Rust クレート、アプリに関するメタデータ、およびその他の Rust 関連の機能を宣言するために使用されます。Rust を使用してアプリのバックエンド開発を行なうつもりがないのであれば、このファイルの変更はあまり行なわないかもしれませんが、このようなファイルがあり、それが何をするのかを知っておくことは重要です。
以下は、Tauri プロジェクトの必要最低限​​の `Cargo.toml` ファイルの例です:
```toml title=Cargo.toml
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "app"
edition = "2021"
rust-version = "1.57"
[build-dependencies]
tauri-build = { version = "2.0.0" }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "2.0.0", features = [ ] }
```
注意を払うべき最も重要な部分は、`tauri-build` と `tauri` の依存関係です。通常、このふたつは両方とも Tauri CLI と同様、最新の「最新バグ修正版(マイナー・バージョン)」である必要がありますが、これは厳密には必須ではありません。アプリの実行中に問題が発生した場合には、すべての Tauri バージョン `tauri` と `tauri-cli`)がそれぞれの「最新バグ修正版」(マイナー・リリースの最新バージョン)であることを確認する必要があります。
Cargo のバージョン番号は [セマンティック・バージョニング] Semver方式[参考](https://ja.wikipedia.org/wiki/セマンティックバージョニング)]を使用しています。`src-tauri` フォルダー内の `cargo update` を実行すると、すべての依存関係に対して Semver 互換の最新バージョンが取得されます。たとえば、`tauri-build` のバージョンとして `2.0.0` を指定すると、Cargo は最新の Semver 互換バージョンである `2.0.0.0` を検出してダウンロードします。Tauri は、重大な変更が導入されるときには「メジャー・バージョン番号」を更新します。つまり、最新のマイナー・バージョンとパッチ・バージョンの更新では、自分のコードが〔最新機能で〕破壊されることを心配することなく、常に安全にアップグレード可能です。
特定のクレート・バージョンを使用したい場合には、依存関係のバージョン番号の前に `=` を追加することで、指定したバージョンを使用できます:
```
tauri-build = { version = "=2.0.0" }
```
さらに注意すべき事は、`tauri` 依存関係の `features=[]` 部分です。`tauri dev` と `tauri build` を実行すると、「Tauri の設定」に基づいてあなたのプロジェクトでどの機能を有効化する必要があるのかを自動的に判断します。`tauri` 機能フラグの詳細については、[こちらのドキュメント(英語版)][tauri Cargo features] を参照してください。
アプリケーションをビルドすると、`Cargo.lock` ファイルが生成されます。このファイルは主に、開発中、マシン同士で同じ依存関係が使用されるようにするために使用されますNode.js の `yarn.lock`、`pnpm-lock.yaml`、あるいは `package-lock.json` と同様です)。一貫性のあるビルドを得るために、このファイルをあなたのソース・リポジトリにコミットすることをお勧めします。
Cargo マニフェスト・ファイルの詳細については、[公式ドキュメント(英語版)][cargo-manifest] を参照してください。
## `package.json`
これは、Node.js で使用されるパッケージ・ファイルです。Tauri アプリのフロントエンドが Node.js ベースの技術(`npm`、`yarn`、`pnpm` など)を使用して開発されている場合、このファイルはフロントエンドの依存関係とスクリプトを構成するために使用されます。
Tauri プロジェクトの必要最低限​​の `package.json` ファイルは次の例のようになります:
```json title=package.json
{
"scripts": {
"dev": "command to start your app development mode",
"build": "command to build your app frontend",
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^2.0.0.0",
"@tauri-apps/cli": "^2.0.0.0"
}
}
```
Tauri アプリケーションで使用されるフロントエンドの起動とビルドに使用されるコマンドの保存には、`"scripts"` セクションを使用するのが一般的です。
上記の `package.json` ファイルでは、「`dev` コマンド」と「`build` コマンド」を指定しています。「`dev` コマンド」はフロントエンド・フレームワークを起動するために `yarn dev` または `npm run dev` を使用して実行され、「`build` コマンド」は本番環境で Tauri によって追加されるフロントエンドの Web アセットをビルドするために `yarn build` または `npm run build` を使用して実行されます。
このようなスクリプトを使用する最も便利な方法は、「[Tauri の設定](#tauri-の設定)」の [beforeDevCommand][before-dev-command] および [beforeBuildCommand][before-build-command] のフックを経由して、Tauri CLI でそのスクリプトをフックすることです:
> > > 《訳注》 **フック** 原文 hooks。プログラム中の特定の箇所フックに、利用者が独自の処理を追加フックできるようにする仕組み[Wikipedia](<https://ja.wikipedia.org/wiki/フック_(プログラミング)>)
```json title=tauri.conf.json
{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build"
}
}
```
:::note
「スクリプト」セクション内の `"tauri"` 指定は `npm` を使用する場合にのみ必要です。
:::
依存関係オブジェクトは、`yarn`、`pnpm install`、あるいは `npm install` のいずれかを実行するときに(この場合 Tauri CLI と API で)、 Node.js がどの依存関係をダウンロードするかを指定します。
`package.json` ファイルに加えて、`yarn.lock`、`pnpm-lock.yaml`、または `package-lock.json` ファイルが表示される場合があります。これらのファイルは、後で依存関係をダウンロードする際に、開発中に使用したものと全く同じバージョンを確実に取得するために役立ちますRust の `Cargo.lock` と同様です)。
「`package.json` ファイル形式」の詳細については、[公式ドキュメント][npm-package](英語版)を参照してください。
[reference の configuration]: /reference/config/
[before-dev-command]: /reference/config/#beforedevcommand-1
[before-build-command]: /reference/config/#beforebuildcommand
[appconfig]: /reference/config/#appconfig
[configure plugins]: /reference/config/#plugins
[セマンティック・バージョニング]: https://semver.org
[cargo-manifest]: https://doc.rust-lang.org/cargo/reference/manifest.html
[npm-package]: https://docs.npmjs.com/cli/v8/configuring-npm/package-json
[tauri Cargo features]: https://docs.rs/tauri/2.0.0/tauri/#cargo-features
[JSON Merge Patch (RFC 7396)]: https://datatracker.ietf.org/doc/html/rfc7396
<div style="text-align: right">
【※ この日本語版は、「Mar 29, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,251 @@
---
title: 開発
description: Tauri を利用したプログラム開発の中心概念
sidebar:
order: 0
label: 概要
i18nReady: true
---
import CommandTabs from '@components/CommandTabs.astro';
[Tauri とは?](/ja/start/) の章ですべての設定を終えたのであれば、Tauri を使用してあなたのアプリケーションを実行する準備が整っています。
「UI フレームワーク」または「JavaScript バンドラー」を使用している場合は、開発プロセスを迅速化する開発サーバーに接続できる可能性があります。そのため、アプリの「開発 URL」とそれを起動する「スクリプト」を設定していない場合は、「Reference参考情報」の章にある [devUrl](/ja/reference/config/#devurl) および [beforeDevCommand](/ja/reference/config/#beforedevcommand) の設定値を使用して設定を行なえます:
```json title=tauri.conf.json
{
"build": {
"devUrl": "http://localhost:3000",
"beforeDevCommand": "npm run dev"
}
}
```
:::note
どのフレームワークにも独自の開発ツールがあります。そのようなツールのすべてを網羅したり、その情報を最新の状態に保つことは、このガイドの目的ではありません。
自分の選択したフレームワークの関連文書を参照して、その内容を学習し正しい設定値を確定してください。
:::
「UI フレームワーク」も「モジュール・バンドラー」も使用していない場合は、Tauri で「フロントエンド・ソース・コード」を読み込むと、Tauri CLI によって開発サーバーが起動されます:
{/* 「Tauri でフロントエンドを読み込む」の部分、原文は "point Tauri to your frontend source code" であるが、pointTauri を〜に向ける)の具体的意味が不明。訳文要検討 */}
```json title=tauri.conf.json
{
"build": {
"frontendDist": "./src"
}
}
```
上記の例では、`src` フォルダーには、フロントエンドによって読み込まれた他のアセットとともに `index.html` ファイルが含まれていなければならない点に注意してください。
:::caution[基本開発サーバーのセキュリティ]
組み込み Tauri 開発サーバーは「相互認証」や「暗号化」をサポートしていません。したがって、信頼できないネットワークでの開発には使用しないでください。
詳細については、[開発サーバーのセキュリティに関する考慮事項](/ja/security/lifecycle/#開発サーバー) を参照してください。
> > > 《訳注》 **基本開発サーバー** 原文は「plain/vanilla Dev Server」。オプションやカスタマイズを一切含まない完全にデフォルト状態のサーバー。
:::
### デスクトップ・アプリケーションの開発
デスクトップ用のアプリケーションを開発するには、`tauri dev` コマンドを実行します。
<CommandTabs
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
deno="deno task tauri dev"
bun="bun tauri dev"
cargo="cargo tauri dev"
/>
初めてこのコマンドを実行する場合、Rust パッケージ・マネージャーが必要なすべてのパッケージをダウンロードしてビルドするのに **数分** かかる場合があります。
これらのデータはキャッシュされており、あなたのコードは再ビルドを必要としているだけなので、その後のビルドは遥かに高速になります。
Rust のビルドが完了すると、Webview が開き、あなたの Web アプリが表示されます。
その Web アプリには変更を加えることができ、ツールがサポートしている場合は、まるでブラウザのように Webview も自動的に更新されます。
#### 「ウェブ・インスペクター」を開く
Webview を右クリックして「検査Inspect」をクリックするか、Windows および Linux ではショートカットの `Ctrl + Shift + I` 、macOS ではショートカットの `Cmd + Option + I` を使用することで、「ウェッブ・インスペクター」を開いてアプリケーションをデバッグすることができます。
### モバイル・アプリケーションの開発
モバイル向けの開発はデスクトップ版の開発と似ていますが、プラットフォームに応じて `tauri android dev` または `tauri ios dev` を実行する必要があります:
<CommandTabs
npm="npm run tauri [android|ios] dev"
yarn="yarn tauri [android|ios] dev"
pnpm="pnpm tauri [android|ios] dev"
deno="deno task tauri [android|ios] dev"
bun="bun tauri [android|ios] dev"
cargo="cargo tauri [android|ios] dev"
/>
初めてこのコマンドを実行する場合、Rust パッケージ・マネージャーが必要なすべてのパッケージをダウンロードしてビルドするのに **数分** かかる場合があります。
これらのデータはキャッシュされており、あなたのコードは再ビルドを必要としているだけなので、その後のビルドは遥かに高速になります。
#### 開発サーバー
モバイル用の開発サーバーもデスクトップ用と同様に動作しますが、iOS の物理デバイス上で実行する場合は、`TAURI_DEV_HOST` 環境変数で定義され、Tauri CLI によって提供される特定のアドレスに応答(リッスン)するように設定する必要があります。
このアドレスは、「パブリック・ネットワーク・アドレス」(デフォルト動作)または iOS 物理デバイスの「TUN アドレス」のいずれかです。後者はより安全ですが、現時点ではデバイスに接続するには Xcode が必要です。
iOS デバイスのアドレスを使用するには、dev コマンドを実行する前に Xcode を開き、「ウィンドウWindow」 > 「デバイスとシミュレータDevices and Simulation」メニューでデバイスがネットワーク経由で接続されていることを確認しなければなりません。
その後、`tauri ios dev --force-ip-prompt` を実行して、iOS デバイスのアドレス(**::2** で終わる IPv6 アドレス)を選択する必要があります。
開発サーバーが iOS デバイスからアクセス可能な正しいホストで応答(リッスン)するようにするには、`TAURI_DEV_HOST` 値が提供されている場合はそれを使用するように設定を微調整する必要があります。以下は Vite の設定例です:
```js
import { defineConfig } from 'vite';
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/
export default defineConfig({
clearScreen: false,
server: {
host: host || false,
port: 1420,
strictPort: true,
hmr: host
? {
protocol: 'ws',
host,
port: 1421,
}
: undefined,
},
});
```
詳細については、各フレームワークの「セットアップ・ガイド」を確認してください。
:::note
[create-tauri-app](https://github.com/tauri-apps/create-tauri-app) を用いて作成したプロジェクトは、そのまま追加設定なしですぐに使用できる、モバイル開発用の開発サーバーが設定されます。
:::
#### デバイスの選択
デフォルトでは、「モバイル dev コマンド」が接続されたデバイスでアプリケーションを実行しようとし、使用するシミュレータを選択するようプロンプトを表示します。実行ターゲットを事前に定義するには、「デバイス名」または「シミュレータ名」を引数に指定します:
<CommandTabs
npm="npm run tauri ios dev 'iPhone 15'"
yarn="yarn tauri ios dev 'iPhone 15'"
pnpm="pnpm tauri ios dev 'iPhone 15'"
deno="deno task tauri ios dev 'iPhone 15'"
bun="bun tauri ios dev 'iPhone 15'"
cargo="cargo tauri ios dev 'iPhone 15'"
/>
#### Xcode または Android Studio を使用する
あるいは、Xcode または Android Studio を使用してアプリケーションを開発することもできます。
これにより、コマンドライン・ツールの代わりに IDE を使用して、開発上の問題のトラブルシューティングを行なえるようになります。
接続されたデバイスまたはシミュレーターで実行する代わりに、モバイル IDE を開くには、`--open` フラグを使用してください:
<CommandTabs
npm="npm run tauri [android|ios] dev --open"
yarn="yarn tauri [android|ios] dev --open"
pnpm="pnpm tauri [android|ios] dev --open"
deno="deno task tauri [android|ios] dev --open"
bun="bun tauri [android|ios] dev --open"
cargo="cargo tauri [android|ios] dev --open"
/>
:::note
アプリケーションを iOS 物理デバイスで実行する場合は、引数 `--host` も指定する必要があり、開発サーバー側ではホストとして `process.env.TAURI_DEV_HOST` 値を使用しなければなりません。
詳細については、各フレームワークの「セットアップ・ガイド」を確認してください。
<CommandTabs
npm="npm run tauri [android|ios] dev --open --host"
yarn="yarn tauri [android|ios] dev --open --host"
pnpm="pnpm tauri [android|ios] dev --open --host"
deno="deno task tauri [android|ios] dev --open --host"
bun="bun tauri [android|ios] dev --open --host"
cargo="cargo tauri [android|ios] dev --open --host"
/>
:::
:::caution[(警告)]
Xcode または Android Studio を使用するには、Tauri CLI プロセスが実行されている**必要があり**、強制終了させることは**できません**。
`tauri [android|ios] dev --open` コマンドを使用して、IDE を閉じるまでそのプロセスを実行したままにしておいてください。
:::
#### 「ウェブ・インスペクター」を開く
- iOS
iOS アプリケーションの「ウェブ・インスペクター」にアクセスするには、Safari を使用する必要があります。
Mac マシンで Safari を開き、メニュー・バーで **Safari > Setting設定** を選択し、**Advanced詳細** をクリックして、**Show features for web developersWeb 開発者向けの機能を表示)** を選択します。
物理デバイスで実行している場合は、**Settings設定 > Safari > Advanced詳細** で **Web Inspectorウェブ・インスペクター** を有効にする必要があります。
すべての手順を実行すると、Safari に **Develop開発** メニューが表示され、そこに、検証すべき接続デバイスとアプリケーションが見つかります。
デバイスまたはシミュレータを選択して **localhostローカルホスト** をクリックし、「Safari 開発ツール」ウィンドウを開きます。
- Android
「インスペクター」は Android エミュレーターではデフォルトで有効になっていますが、物理デバイスでは有効にする必要があります。
Android デバイスをコンピューターに接続し、Android デバイスで **Settings設定** アプリを開き、**Aboutバージョン情報** を選択、「ビルド番号」までスクロールし、それを七回タップします。
これにより、Android デバイスの「開発者モード」と **Developer Options開発者オプション** 設定が有効になります。
あなたのデバイスでアプリケーションのデバッグを有効にするには、**Developer Options開発者向けオプション** 設定に入り、開発者向けオプション・スイッチをオンにして、**USB DebuggingUSB デバッグ)** を有効にする必要があります。
:::note
各 Android ディストリビューションには「開発者モード」を有効にする独自の方法があります。詳細については、それぞれの製造元のドキュメントを確認してください。
:::
Android 用の「Web Inspector」は Google Chrome の DevTools を利用しており、Chrome ブラウザで `chrome://inspect` と検索するとアクセスできます。
Android アプリケーションが実行中の場合、デバイスまたはエミュレータが「リモート・デバイス・リスト」に表示されるはずで、デバイス上で **inspect検査** をクリックすれば「開発者ツール」を開くことができます。
#### トラブル・シューティング
1. Xcode でのビルド・スクリプト実行エラー
Tauri は、Tauri CLI を実行するビルド・フェーズを生成することで iOS Xcode プロジェクトに接続(フック)しますが、これは実行時に読み込まれるライブラリとして Rust ソースをコンパイルするためのものです。
ビルド・フェーズは「Xcode プロセス・コンテキスト」上で実行されるため、PATH の追加などといったシェルの修正を行なえない可能性があります。このため、「Node.js バージョン・マネージャー」などのツールを使用する場合は互換性の問題があるかもしれませんので注意してください。
2. iOS アプリの初回実行時にネットワーク許可を求めるプロンプト
初めて `tauri ios dev` を実行すると、ローカル・ネットワーク上のデバイスを検索して接続する許可を求める iOS のメッセージが表示される場合があります。
iOS デバイスから開発サーバーにアクセスするには、開発サーバーをローカル・ネットワークに公開する必要があるため、この許可が必要となっています。
デバイスでアプリを実行するには、「Allow許可」をクリックしてアプリケーションを再起動する必要があります。
### ソースコードの変更への対応
Webviewがリアルタイムで変更内容を反映するのと同様に、Tauri も Rust ファイルの変化を監視しており、あなたがファイルの変更を行なうと、あなたのアプリケーションは自動的に再ビルドされ再起動されます。
この動作は、`tauri dev` コマンドに `--no-watch` フラグを使用すれば無効化できます。
変更が監視されるファイルを制限するには、「src-tauri」フォルダーに `.taurignore` ファイルを作成します。このファイルは通常の「.gitignore ファイル」と同じように機能するため、任意のフォルダーまたはファイルを無視できます:
```filename=.taurignore
build/
src/generated/*.rs
deny.toml
```
### 「ブラウザ開発ツール」の使用
Tauri の API はアプリ・ウィンドウでのみ機能するため、一旦 Tauri API の使用を開始するとシステムのブラウザーでフロントエンドを開くことができなくなります。
ブラウザの開発者ツールを使用したい場合は、Tauri API の呼び出しを HTTP サーバーを介してブリッジするように、[tauri-invoke-http](https://github.com/tauri-apps/tauri-invoke-http) を設定してください。
### ソース管理
「決定論的なビルド」を提供するために Cargo がロックファイルを使用するので、あなたのプロジェクト・リポジトリで、`src-tauri/Cargo.lock` と `src-tauri/Cargo.toml` を git に**コミットする必要があります**。
そのため、すべてのアプリケーションで `Cargo.lock` をチェックイン(登録)することをお勧めします。`src-tauri/target` フォルダまたはその中のファイルは**コミットしない**でください。
> > > 《訳注》 **決定論的なビルド** 原文は「deterministic builds」。determinstic とは「アルゴリズムに決定性があり、次のステップが常に確定的に一つに決まること」、すなわち、「同じソースコードを用いてコンパイルした場合、確実に同じバイナリデータを再現するビルド手法」のことをいいます詳しくは、Wikipedia [再現性のあるビルド](https://ja.wikipedia.org/wiki/再現性のあるビルド) を参照してください)。
<div style="text-align: right">
【※ この日本語版は、「Mar 29, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,165 @@
---
title: 追加ファイルの埋め込み
sidebar:
order: 1
i18nReady: true
---
フロントエンド(`frontendDist`)に直接含まれていないファイルや、バイナリにインライン化(埋め込み)するには大きすぎるファイルなどを、追加でアプリケーション・バンドルに含める必要がある場合があります。このようなファイルは「`resources`」(リソース)と呼ばれています。
選択したファイルをバンドルするには、`tauri.conf.json` ファイル内の `bundle` オブジェクトに `resources` プロパティを追加します。
`tauri.conf.json` 設定の詳細については、[こちら(英語版)][tauri.bundle] を参照してください。
`resources` プロパティでは、ファイルまたはディレクトリを絶対パスまたは相対パスで指定した対象となるファイルやディレクトリ文字列のリストが必要です。ディレクトリ内のファイルをいくつか一括で指定する必要がある場合は、「グロブ・パターン」が利用できます。
> > > 《訳注》 **グロブ・パターン** 原文は glob patterns。「ワイルドカード文字」を使用してファイル名フォルダ名のパターンを指定する方法。詳しくは「[Wikipedia](https://ja.wikipedia.org/wiki/グロブ)」を参照してください。
以下の例は、設定内容を説明するためのものです。すべての内容が備わった完全な `tauri.conf.json` ファイルではありませんので注意してください:
```json title=tauri.conf.json
{
"bundle": {
"resources": [
"/absolute/path/to/textfile.txt",
"relative/path/to/jsonfile.json",
"resources/**/*"
]
}
}
```
あるいは、ファイルのコピー先を変更したいのであれば、`resources` 設定で「マップ・オブジェクト」を指定することもできます。以下の例は、異なるソースからのファイルを、同じ `resources` フォルダーに取り込む方法を示したものです:
> > > 《訳注》 **マップ・オブジェクト** 原文「a map object」。キーと値を一対一でマッピングするためのデータ構造。
```json title=tauri.conf.json
{
"bundle": {
"resources": {
"/absolute/path/to/textfile.txt": "resources/textfile.txt",
"relative/path/to/jsonfile.json": "resources/jsonfile.json",
"resources/**/*": "resources/"
}
}
}
```
:::note
Tauri の [アクセス権の仕組み](/reference/acl/capability/) では、絶対パスと親コンポーネント (`../`) を含むパスは `"$RESOURCE/**"` 経由でのみ許されています。`"path/to/file.txt"` のような相対パスは、`"$RESOURCE/path/to/file.txt"` のように明示的に指定すれば使用可能です。
:::
## ソース・パス構文
以下の説明では、「ターゲット・リソース・ディレクトリ」(対象ディレクトリ)とは、オブジェクト表記法では「コロンの後の値」、または配列表記では「元のファイル・パスを再構築したもの」のどちらかです。
> > > 《訳注》 **オブジェクト表記法** 原文は the object notation。軽量なテキストベースのデータ交換用フォーマット。詳しくは Wikipedia の「[JavaScript Object Notation (JSON)](https://ja.wikipedia.org/wiki/JavaScript_Object_Notation)」などを参照してください。
> > > **配列表記** 原文 the array notation。巨大数の表記法のこと
- `"dir/file.txt"`: この表記では、`file.txt` ファイルを「ターゲット・リソース・ディレクトリ」にコピーします。
- `"dir/"`: この表記では、すべてのファイル**とディレクトリ**を*再帰的に(そのまま丸ごと)*「ターゲット・リソース・ディレクトリ」にコピーします。現在のファイルとディレクトリの「ファイル・システムの階層構造」もそのまま保持する場合にもこれを利用できます。
- `"dir/*"`: この表記では `dir` ディレクトリ内の「すべてのファイル」を*非再帰的に*(すなわち、そのままの形ではなく、サブディレクトリが無視されて)「ターゲット・リソース・ディレクトリ」にコピーします。
- `"dir/**`: この表記では `**` が「ディレクトリのみに一致」を意味するため、ファイルが見つからないことになり「エラー」がスローされます。
- `"dir/**/*"`: この表記では `dir` ディレクトリ内の「すべてのファイル」(`dir/` 内のすべてのファイルとすべてのサブディレクトリ内のすべてのファイル)を「ターゲット・リソース・ディレクトリ」に*再帰的に*(そのままの形で)コピーします。
- `"dir/**/**`: この表記では `**` は「ディレクトリのみに一致」を意味するため、ファイルが見つからないことになり「エラー」がスローされます。
## Rustのファイルへのアクセス
次の例は、以下のような「i18n」国際化対応の json ファイルを追加バンドルする場合です:
> > > 《訳注》 **i18n** i18n = internationalization国際化。以下の例では「ドイツ語対応言語コード de」の de.json を追加し、「こんにちはhello」と「さようならbye」に対応するドイツ語を追記しています。
```json title=de.json
{
"hello": "Guten Tag!",
"bye": "Auf Wiedersehen!"
}
```
この場合、こうしたファイルは `tauri.conf.json` の隣の `lang`(言語)ディレクトリに保存されます。
このため、上述のように `resources` に `"lang/*"` を追加します。
Rust 側では、[`App`] と [`AppHandle`] から取得できる [`PathResolver`] のインスタンスが必要です:
```rust
tauri::Builder::default()
.setup(|app| {
// 指定するパスは、`tauri.conf.json > bundle > resources` で定義されているのと
// 同じ構文に従う必要があります。
let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource)?;
let file = std::fs::File::open(&resource_path).unwrap();
let lang_de: serde_json::Value = serde_json::from_reader(file).unwrap();
// 次の一文で「'Guten Tag!'」がターミナルにプリントされます
println!("{}", lang_de.get("hello").unwrap());
Ok(())
})
```
```rust
#[tauri::command]
fn hello(handle: tauri::AppHandle) -> String {
let resource_path = handle.path().resolve("lang/de.json", BaseDirectory::Resource)?;
let file = std::fs::File::open(&resource_path).unwrap();
let lang_de: serde_json::Value = serde_json::from_reader(file).unwrap();
lang_de.get("hello").unwrap()
}
```
## JavaScript のファイルにアクセス
以下は上記の例を基にしています。
`$RESOURCE` フォルダへのアクセス権と、必要な [`plugin-fs`] API を有効にするためには、アクセス制御リストの設定が必要であることに注意してください:
```json title=src-tauri/capabilities/default.json ins={14-15}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"fs:allow-read-text-file",
"fs:allow-resource-read-recursive"
]
}
```
:::note
この例の、`fs:allow-resource-read-recursive` は、`$RESOURCE` フォルダー、ファイル、およびサブディレクトリ全体への完全な再帰読み取りアクセスを許可するために使用しています。
この他のオプションについては [アクセス権の設定] を、よりきめ細かな制御については [スコープ(適用範囲)] をお読みください。
:::
```javascript
import { resolveResource } from '@tauri-apps/api/path';
import { readTextFile } from '@tauri-apps/plugin-fs';
const resourcePath = await resolveResource('lang/de.json');
const langDe = JSON.parse(await readTextFile(resourcePath));
console.log(langDe.hello); // This will print 'Guten Tag!' to the devtools console
```
[tauri.bundle]: /reference/config/#bundleconfig
[`pathresolver`]: https://docs.rs/tauri/latest/tauri/path/struct.PathResolver.html
[`app`]: https://docs.rs/tauri/latest/tauri/struct.App.html
[`apphandle`]: https://docs.rs/tauri/latest/tauri/struct.AppHandle.html
[`plugin-fs`]: /reference/javascript/fs/
[アクセス権の設定]: /ja/plugin/file-system/#scopes
[スコープ(適用範囲)]: /ja/reference/acl/scope/
<div style="text-align: right">
【※ この日本語版は、「Mar 13, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,251 @@
---
title: 外部バイナリの埋め込み
sidebar:
order: 1
i18nReady: true
---
アプリケーションに機能を追加したり、ユーザーが追加の依存関係Node.jsやPythonなどをインストールできないようにしたりするために、外部バイナリを埋め込む必要があるかもしれません。このようなバイナリを「**サイドカー** `sidecar`」と呼びます。
バイナリは、任意のプログラミング言語で記述された実行ファイルです。一般的な使用例としては、`pyinstaller` を使用してバンドルされた Python CLI アプリケーションや API サーバーなどが挙げられます。
お望みのバイナリをバンドルするには、`tauri.conf.json` の `tauri > bundle` オブジェクトに `externalBin` プロパティを追加します。
`externalBin` の設定では、絶対パスまたは相対パスのいずれかを使用して対象のバイナリを指定する「文字列のリスト」が必要です。
以下は、「サイドカー」の設定を説明するための「Tauri の設定」の抜粋(スニペット)です:
```json title="src-tauri/tauri.conf.json"
{
"bundle": {
"externalBin": [
"/absolute/path/to/sidecar",
"../relative/path/to/binary",
"binaries/my-sidecar"
]
}
}
```
:::note
相対パスは、`src-tauri` ディレクトリにある `tauri.conf.json` ファイルを基準に表記します。
したがって、`binaries/my-sidecar` とは `<PROJECT ROOT>/src-tauri/binaries/my-sidecar` ということを表しています。
:::
サポートされているどのアーキテクチャでも外部バイナリが動作するようにするには、指定されたパスに「同じ名前」と「接尾辞 `-$TARGET_TRIPLE`」(ターゲット・トリプル)を持つバイナリが存在していなければなりません。
たとえば、`"externalBin": ["binaries/my-sidecar"]` の場合、Linux では実行可能ファイル「`src-tauri/binaries/my-sidecar-x86_64-unknown-linux-gnu`」が、Apple Silicon を搭載した Mac OS では実行可能ファイル「`src-tauri/binaries/my-sidecar-aarch64-apple-darwin`」が必要となります。
> > > 《訳注》 **ターゲット・トリプル** 原文は target triple。Rust でクロスコンパイルを行なう場合のアーキテクチャの指定方式で、コンパイル対象targetを三つtripleの項目「CPU ファミリー名」「メーカー名」「OS 名」で指定します。上記の説明例では `aarch64-apple-darwin` などのようにハイフンで結ばれた表記になっています。
次のコマンドで表示される「`host:`」プロパティを見ることで、**現在の** プラットフォームの「接尾辞 `-$TARGET_TRIPLE`」が判ります:
```sh
rustc -Vv
```
ほとんどの Unix システムに搭載されている `grep` コマンドと `cut` コマンドが使用できる場合は、次のコマンドでターゲット・トリプルを直接抽出できます:
```shell
rustc -Vv | grep host | cut -f2 -d' '
```
Windows では shell の代わりに PowerShell を使用します:
```powershell
rustc -Vv | Select-String "host:" | ForEach-Object {$_.Line.split(" ")[1]}
```
次の例は、ターゲット・トリプルをバイナリに追加する Node.js スクリプトです。
```javascript
import { execSync } from 'child_process';
import fs from 'fs';
const extension = process.platform === 'win32' ? '.exe' : '';
const rustInfo = execSync('rustc -vV');
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
if (!targetTriple) {
console.error('Failed to determine platform target triple');
}
fs.renameSync(
`src-tauri/binaries/sidecar${extension}`,
`src-tauri/binaries/sidecar-${targetTriple}${extension}`
);
```
このスクリプトは、それが実行されているアーキテクチャとは異なるアーキテクチャ向けにコンパイルした場合は機能しないので、自分用ビルド・スクリプトを作成するための出発点として使用してください。
## Rust から実行する
:::note
プラグインを正しく設定して初期化するには、まず[シェル・プラグイン・ガイド](/ja/plugin/shell/)に従ってください。
プラグインを初期化し、構成していないと、以下の例は機能しません。
:::
Rust 側では、`tauri_plugin_shell::ShellExt` トレイトをインポートし、AppHandle で `shell().sidecar()` 関数を呼び出します:
> > > 《訳注》 **トレイト** 原文は trait性格の「特徴」「特質」、遺伝的な「形質」を表す語。『[Rust 日本語版](https://doc.rust-jp.rs/book-ja/ch10-02-traits.html)』では、異なる型に対して共通の振る舞いを定義する、というような説明が行なわれています。「型の形質」=「型質?」
```rust
use tauri_plugin_shell::ShellExt;
use tauri_plugin_shell::process::CommandEvent;
let sidecar_command = app.shell().sidecar("my-sidecar").unwrap();
let (mut rx, mut _child) = sidecar_command
.spawn()
.expect("Failed to spawn sidecar");
tauri::async_runtime::spawn(async move {
// stdout標準出力などのイベントを読み取ります
while let Some(event) = rx.recv().await {
if let CommandEvent::Stdout(line_bytes) = event {
let line = String::from_utf8_lossy(&line_bytes);
window
.emit("message", Some(format!("'{}'", line)))
.expect("failed to emit event");
// stdin標準入力に書き込みます
child.write("message from Rust\n".as_bytes()).unwrap();
}
}
});
```
:::note
`sidecar()` 関数には「ファイル名」だけが必要で、配列 `externalBin` に設定されている「パス」自体は必要では**ありません**。
たとえば、次のような設定になっているとします:
```json title="src-tauri/tauri.conf.json"
{
"bundle": {
"externalBin": ["binaries/app", "my-sidecar", "../scripts/sidecar"]
}
}
```
適切に「サイドカー」を実行するには、`app.shell().sidecar(name)` を呼び出します。このとき、`name` は、たとえば `"binaries/app"` のようなパスではなく、`"app"`、`"my-sidecar"`、または `"sidecar"` のいずれかになります。
:::
このコードを Tauri コマンド内に配置すれば、AppHandle を簡単に渡すことができ、ビルダー・スクリプト内に AppHandle への参照を保存すれば、アプリケーション内のどこからでも AppHandle へアクセス可能になります。
## JavaScript から実行する
「サイドカー」を実行する場合、Tauri では、子プロセスで `execute` または `spawn` メソッドを実行する権限を「サイドカー」に付与する必要があります。この権限を付与するには、ファイル `<PROJECT ROOT>/src-tauri/capabilities/default.json` に移動し、以下のセクションを権限配列に追加します。前述の「Note」欄で説明した「相対パスの記述方法」に従って「サイドカー」を記述することを忘れないでください。
```json title="src-tauri/capabilities/default.json" ins={4-13}
{
"permissions": [
"core:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "binaries/app",
"sidecar": true
}
]
},
"shell:allow-open"
]
}
```
:::note
サイドカーの子プロセスは `command.execute()` メソッドを使用して開始されるため、`shell:allow-execute` 識別子が使用されます。これを `command.spawn()` で実行するには、識別子を `shell:allow-spawn` に変更するか、上記と同じ構造ですが識別子を `shell:allow-spawn` に変更した別のエントリを配列に追加する必要があります。
:::
JavaScript コード内では、`@tauri-apps/plugin-shell` モジュールから `Command` クラスをインポートし、`sidecar` 静的メソッドを使用します。
```javascript
import { Command } from '@tauri-apps/plugin-shell';
const command = Command.sidecar('binaries/my-sidecar');
const output = await command.execute();
```
:::note
`Command.sidecar` に送られる文字列は、`externalBin` 設定配列で定義されている文字列のいずれかと一致しなければなりません。
:::
## 引数の受け渡し
通常の [コマンド][std::process::Command] を実行する場合と同じように、Sidecar のコマンドに引数を渡すことができます。
引数は **静的 static**(たとえば `-o` または `serve`)または **動的 dynamic**(たとえば `<file_path>` または `localhost:<PORT>`)のいずれかになります。引数は、呼び出す順番通りに定義します。「静的引数」はそのまま定義されますが、「動的引数」は正規表現を使用して定義します。
まず、`src-tauri/capabilities/default.json` に、サイドカー・コマンドに渡す必要がある引数を定義します:
```json title="src-tauri/capabilities/default.json" ins={8-25}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"args": [
"arg1",
"-a",
"--arg2",
{
"validator": "\\S+"
}
],
"name": "binaries/my-sidecar",
"sidecar": true
}
]
},
"shell:allow-open"
]
}
```
:::note
もし Tauri v1 版から移行している場合は、Tauri v2 CLI の `migrate` コマンドでこの処理が実行できるはずです。詳細については、「Tauri 1.0 からのアップグレード」の [自動移行](/ja/start/migrate/from-tauri-1/#自動移行) の項をお読みください。
:::
あとは、サイドカー・コマンドを呼び出すには、**すべての**引数を配列として渡すだけです。
Rust では:
```rust
use tauri_plugin_shell::ShellExt;
#[tauri::command]
async fn call_my_sidecar(app: tauri::AppHandle) {
let sidecar_command = app
.shell()
.sidecar("my-sidecar")
.unwrap()
.args(["arg1", "-a", "--arg2", "any-string-that-matches-the-validator"]);
let (mut _rx, mut _child) = sidecar_command.spawn().unwrap();
}
```
JavaScript では:
```javascript
import { Command } from '@tauri-apps/plugin-shell';
// 引数配列は `capabilities/default.json` で指定されたものと完全に一致することに注意してください。
const command = Command.sidecar('binaries/my-sidecar', [
'arg1',
'-a',
'--arg2',
'any-string-that-matches-the-validator',
]);
const output = await command.execute();
```
[std::process::Command]: https://doc.rust-lang.org/std/process/struct.Command.html
<div style="text-align: right">
【※ この日本語版は、「Jan 06, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,206 @@
---
title: 状態管理
sidebar:
order: 1
i18nReady: true
---
Tauri アプリケーションでは、しばしば、アプリケーションの現在の状態を追跡したり、アプリケーションに関連付けられたさまざまな物事のライフサイクルを管理したりする必要があります。Tauri では、[`Manager`] API を使用してアプリケーションの状態を管理し、コマンドが呼び出されたときの状態を読み取るための簡単な方法を用意しています。
以下に簡単な例を示します:
```rust
use tauri::{Builder, Manager};
struct AppData {
welcome_message: &'static str,
}
fn main() {
Builder::default()
.setup(|app| {
app.manage(AppData {
welcome_message: "Welcome to Tauri!",
});
Ok(())
})
.run(tauri::generate_context!())
.unwrap();
}
```
[`Manager`] トレイトを実装するどの型でも(たとえば [`App`] インスタンス)、その状態に後からアクセスできるようになります:
```rust
let data = app.state::<AppData>();
```
各コマンドの状態へのアクセスを含めて、より詳しい情報については、[状態へのアクセス](#状態へのアクセス)セクションを参照してください。
## 可変性
Rust では、複数のスレッド間で共有されている値や、所有権が [`Arc`](または Tauri の [`State`])などの共有ポインターを通して制御されている値を直接変更することはできません。それを行なうと、(たとえば、二つの書き込みが同時に発生するなどの)データ競合が発生する可能性があるからです。
これを回避するために、[**内部可変性**](https://doc.rust-jp.rs/book-ja/ch15-05-interior-mutability.html) と呼ばれる概念を使用します。たとえば、標準ライブラリの [`Mutex`](ミューテックス)は「状態をラップする」ために使うことができます。これにより、ある値を変更する必要があるときにそれをロックし、変更が終わったらそのロックを解除します。
> > > 《訳注》 **Mutex** 「Mutex」は"mutual exclusion"(相互排他)の省略形で、どんな時も1つのスレッドにしかなんらかのデータへのアクセスを許可しない仕組みです。「`Mutex` ミューテックス」の詳しい説明は [Rust Book 16.3](https://doc.rust-jp.rs/book-ja/ch16-03-shared-state.html) を参照してください。
```rust
use std::sync::Mutex;
use tauri::{Builder, Manager};
#[derive(Default)]
struct AppState {
counter: u32,
}
fn main() {
Builder::default()
.setup(|app| {
app.manage(Mutex::new(AppState::default()));
Ok(())
})
.run(tauri::generate_context!())
.unwrap();
}
```
「ミューテックス」をロックすることで状態を変更できるようになります:
```rust
let state = app.state::<Mutex<AppState>>();
// ミューテックスをロックして可変アクセスを取得します:
let mut state = state.lock().unwrap();
// 状態を変更します:
state.counter += 1;
```
スコープの終了時、または `MutexGuard` が削除されると、「ミューテックス」は自動的にロック解除され、アプリケーションの他の部分が内部のデータにアクセス可能となり、そのデータの変更ができるようになります。
### 非同期ミューテックスを使用する機会
> > > 《訳注》 **Tokio** 「Tokio」は Rust 用非同期ランタイム・ライブラリ。
[Tokio の `Mutex`](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html#which-kind-of-mutex-should-you-use) ドキュメントから引用すると、以下のように、Tokio が提供するような非同期ミューテックスの代わりに、標準ライブラリの [`Mutex`] を使用しても大丈夫なことが多いようです:
> 一般に信じられているのとは反対に、非同期コードで標準ライブラリの通常の Mutex を使用しても問題はなく、しばしばそれが好まれています。 ... 非同期ミューテックスの主要な使用事例は、データベース接続などの IO リソースへの共有可変アクセスを提供することです。
この二つの `Mutex` 間のトレードオフ(一得一失)を理解するには、それぞれの上記リンク先ドキュメントを十分に読み込むことをお勧めします。非同期ミューテックスが必要に*なるであろう*理由の一つは、「待機ポイント」を跨いで `MutexGuard` を保持する必要がある場合です。
### `Arc` は必要?
> > > 《訳注》 **Arc** 「[構造体 Arc](https://doc.rust-jp.rs/rust-by-example-ja/std/arc.html)」Struct `Arc`。Atomically Reference Counted自動参照カウントの略。
Rust では、複数のスレッド間で値の所有権を共有するために [`Arc`] が使用されるのが一般的です(通常は `Arc<Mutex<T>>` の形で、 [`Mutex`] とペアで用いられます)。ただし、[`State`] に保存されているものについては [`Arc`] を使用する必要はありません。なぜならば Tauri があなたに代ってこれを実行するからです。
`State` のライフタイム要件により「状態」を新しいスレッドに移動できない場合は、代わりに `AppHandle` をその新しいスレッドに移動して、以下の「[Manager トレイトを使用して状態にアクセスする](#manager-トレイトを使用して状態にアクセスする)」の項で示すように「状態」を取得できます。`AppHandle` は、このようなユースケース(使用事例)では意図的にクローン化が安価になっています。
> > > 《訳注》 **意図的にクローン化が安価** 原文 deliberately cheap to clone。文意不詳 `AppHandle` で同様の処理を簡便に行なえるようにしているということ?)
## 状態へのアクセス
### コマンド内の状態へのアクセス
```rust
#[tauri::command]
fn increase_counter(state: State<'_, Mutex<AppState>>) -> u32 {
let mut state = state.lock().unwrap();
state.counter += 1;
state.counter
}
```
コマンドの詳細については、「[フロントエンドから Rust を呼び出す](/ja/develop/calling-rust/)」の章を参照してください。
#### 非同期コマンド
`async` コマンドを使用していて、Tokio の非同期 [`Mutex`](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html) を利用したい場合は、以下のように、同様の方法で設定すれば「状態」にアクセスできます。
```rust
#[tauri::command]
async fn increase_counter(state: State<'_, Mutex<AppState>>) -> Result<u32, ()> {
let mut state = state.lock().await;
state.counter += 1;
Ok(state.counter)
}
```
非同期コマンドを使用する場合、戻り値の型は [`Result`] である必要があることに注意してください。
### [`Manager`] トレイトを使用して状態にアクセスする
場合によっては、別のスレッド内や `on_window_event` のようなイベント・ハンドラー内といった、コマンドの外側にある「状態」にアクセスする必要が生じることがあります。そのような場合には、[`Manager`] トレイトを実装する型(`AppHandle` など)の `state()` メソッドを使用して、状態を取得できます:
```rust
use std::sync::Mutex;
use tauri::{Builder, Window, WindowEvent, Manager};
#[derive(Default)]
struct AppState {
counter: u32,
}
// イベント・ハンドラ内:
fn on_window_event(window: &Window, _event: &WindowEvent) {
// グローバル状態を取得できるように、アプリへのハンドルを取得します。
let app_handle = window.app_handle();
let state = app_handle.state::<Mutex<AppState>>();
// ミューテックスをロックして「状態」に可変的にアクセスします。
let mut state = state.lock().unwrap();
state.counter += 1;
}
fn main() {
Builder::default()
.setup(|app| {
app.manage(Mutex::new(AppState::default()));
Ok(())
})
.on_window_event(on_window_event)
.run(tauri::generate_context!())
.unwrap();
}
```
この方法は、コマンド・インジェクション(外部からのコマンド実行)に依存できない場合に有用です。たとえば、`AppHandle` を使用した方が簡単なスレッドに「状態」を移動する必要がある場合や、コマンド・コンテキスト内に存在していない場合などです。
> > > 《訳注》 **コマンド・インジェクションに依存できない** 原文は cannot rely on command injection。文意不詳
> > > 《訳注》 **コマンド・コンテキスト** 原文 a command context。コマンドの実行に必要な情報や環境。
## 型の不一致
:::caution
構造体 [`State`] のパラメータに間違った型を使用すると、「コンパイル時エラー」ではなく「実行時パニック」が発生します。
たとえば、`State<'_, Mutex<AppState>>` の代わりに `State<'_, AppState>` を使用すると、その型で管理される「状態」が存在しないことになります。
:::
必要であれば、この間違いを防ぐために、「状態」を「[型エイリアス](https://doc.rust-jp.rs/book-ja/ch19-04-advanced-types.html)」でラップすることもできます:
```rust
use std::sync::Mutex;
#[derive(Default)]
struct AppStateInner {
counter: u32,
}
type AppState = Mutex<AppStateInner>;
```
ただし、型エイリアスはそのまま使用し、[`Mutex`] で再度ラップしないようにしてください。そうしないと、同じ問題を引き起こすことになります。
[`Manager`]: https://docs.rs/tauri/latest/tauri/trait.Manager.html
[`State`]: https://docs.rs/tauri/latest/tauri/struct.State.html
[`Mutex`]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html
[`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
[`App`]: https://docs.rs/tauri/latest/tauri/struct.App.html
[`Result`]: https://doc.rust-lang.org/stable/std/result/index.html
<div style="text-align: right">
【※ この日本語版は、「Mar 28, 2025 英語版」に基づいています】
</div>

View File

@@ -0,0 +1,62 @@
---
title: 依存関係の更新
sidebar:
order: 1
i18nReady: true
---
{/* TODO: Add plugin update example */}
import CommandTabs from '@components/CommandTabs.astro';
## npm パッケージの更新
`tauri` パッケージを使用している場合:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn up @tauri-apps/cli @tauri-apps/api"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
/>
以下のコマンドを使用すれば、コマンド・ラインで Tauri の最新バージョンを検出することもできます。
<CommandTabs
npm="npm outdated @tauri-apps/cli"
yarn="yarn outdated @tauri-apps/cli"
pnpm="pnpm outdated @tauri-apps/cli"
/>
## Cargo パッケージの更新
古いパッケージは、[`cargo outdated`] または crates.io ページ [tauri] / [tauri-build] で確認できます。
`src-tauri/Cargo.toml` ファイルに移動し、`tauri` と `tauri-build` を次のように変更します:
```toml
[build-dependencies]
tauri-build = "%version%"
[dependencies]
tauri = { version = "%version%" }
```
ここで、`%version%` は [tauri-build] / [tauri] の対応するバージョン番号のことです。
次に、次の操作を実行します:
```shell
cd src-tauri
cargo update
```
あるいは、[cargo-edit] が提供する `cargo upgrade` コマンドを実行して、こうした処理をすべて自動的に実行することもできます。
[`cargo outdated`]: https://github.com/kbknapp/cargo-outdated
[tauri]: https://crates.io/crates/tauri/versions
[tauri-build]: https://crates.io/crates/tauri-build/versions
[cargo-edit]: https://github.com/killercup/cargo-edit
<div style="text-align: right">
【※ この日本語版は、「Jul 23, 2024 英語版」に基づいています】
</div>

View File

@@ -1186,7 +1186,7 @@ Tauri バージョン 1v1では、外部バイナリとその引数は許
JavaScript 関連では、[Shell プラグインの移行](#shell-プラグインの移行) の内容を必ず実行して下さい。
Rust 関連では、`tauri::api::process` API が削除されています。代わりに `tauri_plugin_shell::ShellExt` および `tauri_plugin_shell::process::CommandEvent` API を使用してください。使用法については、[外部バイナリの埋め込み](/ja/develop/sidecar/#running-it-from-rust) ガイドを参照して下さい。
Rust 関連では、`tauri::api::process` API が削除されています。代わりに `tauri_plugin_shell::ShellExt` および `tauri_plugin_shell::process::CommandEvent` API を使用してください。使用法については、[外部バイナリの埋め込み](/ja/develop/sidecar/#rust-から実行する) ガイドを参照して下さい。
「process-command-api」機能フラグは バージョン 2v2で削除されました。そのため、外部バイナリの実行に、この機能を Tauri 構成で定義する必要がなくなりました。

View File

@@ -12,12 +12,12 @@ import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Tauri 1.0 からのアップグレード"
href="../start/migrate/from-tauri-1/"
href="/ja/start/migrate/from-tauri-1/"
description="バージョン 1.0 のプロジェクトをバージョン 2.0 にアップグレードするために必要なアップデートについて"
/>
<LinkCard
title="Tauri 2.0 ベータ版からの移行"
href="../start/migrate/from-tauri-2-beta/"
href="/ja/start/migrate/from-tauri-2-beta/"
description="2.0 ベータ版プロジェクトを 2.0 正規版にアップグレードするために必要なアプデートについて"
/>
</CardGrid>