チャットや同期ツールのように裏で動いていてほしいアプリには、設定画面に「ログイン時に起動する」のスイッチを用意するのが定番です。Tauri では公式の Autostart プラグインを使い、JS の enable() / disable() / isEnabled() か、Rust の app.autolaunch() で登録を切り替えます。登録はユーザーごとで、正確には OS の起動時ではなくそのユーザーがログインしたときに起動し、管理者権限は要りません。自動起動のときだけ画面を出さずに常駐させる方法と、開発中に試すときの落とし穴も説明します。
前提条件
プラグインを追加します。Windows / macOS / Linux 用で、iOS / Android では使えません。
npm run tauri add autostart
依存の追加に加えて、lib.rs へのプラグインの登録と、デスクトップ向けの capability ファイル(capabilities/desktop.json)への autostart:default の追加まで行われます。autostart:default は autostart:allow-enable / allow-disable / allow-is-enabled の 3 つをまとめたもので、core:default には含まれません。自分で書く場合は次のようにします。
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"],
"windows": ["main"],
"permissions": ["autostart:default"]
}
platforms でデスクトップに限るのは、モバイル向けのビルドにはこのプラグインが含まれないためです。2 章のように Rust のコマンドで切り替えるなら、JS 側の権限は無しにもできます。
設定ファイルで自動起動を有効にする項目はありません。ユーザーの環境を変える操作なので、初回起動時に黙って enable() せず、ユーザーが選んだときに呼びます。
1. フロントエンドから実装する (TypeScript)
設定画面のチェックボックスに結び付ける例です。表示の元にするのはアプリ側に保存した値ではなく、毎回 isEnabled() で読んだ OS 側の状態です。ユーザーが OS の設定画面から自動起動を外すことがあるためです。失敗したときも見た目が実際とずれないよう、最後に読み直します。
import { enable, disable, isEnabled } from '@tauri-apps/plugin-autostart';
// <input type="checkbox" id="autostart"> を想定
export async function bindAutostartToggle() {
const toggle = document.querySelector<HTMLInputElement>('#autostart');
if (!toggle) return;
toggle.checked = await isEnabled(); // 画面を開くたびに OS 側の状態を読む
toggle.addEventListener('change', async () => {
toggle.disabled = true;
try {
if (toggle.checked) {
await enable();
} else if (await isEnabled()) {
await disable(); // 登録が無いときに呼ぶと失敗する環境があるので確かめてから
}
} catch (e) {
console.error('自動起動を切り替えられませんでした:', e);
} finally {
toggle.checked = await isEnabled(); // 失敗しても実際の状態に合わせ直す
toggle.disabled = false;
}
});
}
2. バックエンドから実装する (Rust)
起動引数を付けて登録し、自動起動を見分ける
「ログイン直後は画面を出さずにトレイに常駐し、自分で起動したときは画面を出す」には、Builder の args() で自動起動のときだけ付く引数を登録し、起動時にその有無を見ます。メインウィンドウは "visible": false にしておき、手動で起動したときだけ show() します。
{
"app": {
"windows": [
{ "label": "main", "title": "My App", "visible": false }
]
}
}
tauri add が lib.rs に書き足した登録の行は消して、次のコードに置き換えます。二重に登録すると先に登録した側の設定が使われ、args() の引数が付かないまま OS に登録されます。
use tauri::Manager;
use tauri_plugin_autostart::ManagerExt;
/// 自動起動のときだけ付ける引数(登録と判定で同じ値を使う)
const AUTOSTART_FLAG: &str = "--autostart";
fn started_by_autostart() -> bool {
std::env::args().any(|a| a == AUTOSTART_FLAG)
}
/// 画面から「自動起動で立ち上がったか」を問い合わせる
#[tauri::command]
fn launched_by_autostart() -> bool {
started_by_autostart()
}
/// 自動起動を切り替え、切り替え後の実際の状態を返す
#[tauri::command]
fn set_autostart(app: tauri::AppHandle, enabled: bool) -> Result<bool, String> {
let autostart = app.autolaunch();
let current = autostart.is_enabled().map_err(|e| e.to_string())?;
if enabled && !current {
autostart.enable().map_err(|e| e.to_string())?;
} else if !enabled && current {
autostart.disable().map_err(|e| e.to_string())?;
}
autostart.is_enabled().map_err(|e| e.to_string())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// デスクトップ専用のプラグインなので cfg(desktop) の中で登録する
#[cfg(desktop)]
app.handle().plugin(
tauri_plugin_autostart::Builder::new()
.args([AUTOSTART_FLAG])
.build(),
)?;
// 手動で起動したときだけ画面を出す(自動起動ならトレイに常駐させておく)
if !started_by_autostart() {
if let Some(main) = app.get_webview_window("main") {
main.show()?;
}
}
Ok(())
})
.invoke_handler(tauri::generate_handler![set_autostart, launched_by_autostart])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Rust での入口は app.autolaunch() です(autostart ではありません)。enable() などは JS と同じ働きで、トレイのメニューから切り替えるときに使えます。引数の読み方は sys-013、トレイは menu-009 を参照してください。裏で動いている間にユーザーがアイコンから起動すると 2 つ目のプロセスが立ち上がるので、Single Instance プラグイン で既存のウィンドウを出すのが定番です。
macOS の登録方式(後述)を変えるときは tauri_plugin_autostart::init(MacosLauncher::AppleScript, Some(vec!["--autostart"])) を使います。Builder の macos_launcher() は macOS 向けのビルドにしか無く、Windows や Linux ではコンパイルエラーになります。
JS からは次のように呼びます。この形なら JS 側に autostart の権限は要りません。
import { invoke } from '@tauri-apps/api/core';
const enabled = await invoke<boolean>('set_autostart', { enabled: true });
console.log(`自動起動: ${enabled ? '有効' : '無効'}`);
if (await invoke<boolean>('launched_by_autostart')) {
console.log('ログイン時に自動で起動しました'); // 起動直後のお知らせを出さない、など
}
動作確認
npm run tauri dev で起動し、チェックボックスをオンにすると isEnabled() が true になり、Windows ならタスクマネージャーの「スタートアップ アプリ」に項目が現れます。自動起動したときの動きは、引数を付けて起動すれば再ログインせずに確かめられます。npm run と tauri dev(cargo 用とアプリ用)がそれぞれ -- を区切りに使うので、3 つ重ねます。
npm run tauri dev -- -- -- --autostart
ウィンドウが表示されず、launched_by_autostart が true を返せば成功です。本当にログイン時に起動するかは、インストーラーで入れたリリースビルドでオンにし、サインアウトしてサインインし直して確かめます。
よくあるエラーと対処法
- 「autostart.enable not allowed. Permissions associated with this command: autostart:allow-enable, autostart:default」: 呼び出したウィンドウに権限がありません。リリースビルドでは「Command plugin:autostart|enable not allowed by ACL」だけになります。
- 「state() called before manage() for」で始まるパニック: プラグインを登録していないか、登録より先に
app.autolaunch()を呼んでいます。 - 自動起動したのに
--autostartが付いていない: プラグインを二重に登録しています。また、引数はenable()の時点で OS に書き込まれるので、コードの引数を変えたら一度オフにしてからオンにし直します。 - ログイン時に起動したが画面が真っ白、または接続できない趣旨の表示になる:
tauri dev中にオンにしたため、開発用の実行ファイル(src-tauri/target/debugの中)が登録されています。開発用のビルドは開発サーバーからページを読むため、サーバーが動いていないと表示できません。試した後はオフに戻します。 disable()がエラーになる: 登録が無い状態で呼ぶと、環境によっては見つからない趣旨のエラーになります。isEnabled()で確かめてから呼びます。
OS ごとの違いと注意点
- Windows: 登録されるのは起動中の実行ファイルのパスで、インストール先が変わったらオンにし直す必要があります。NSIS のインストーラーはアンインストール時に
productNameと同じ名前の登録も消しますが、app_name()で名前を変えると対象外になり、MSI にはこの処理がありません。 - macOS: 既定の
MacosLauncher::LaunchAgentは実行ファイルを直接起動する登録で、args()の引数も渡ります。MacosLauncher::AppleScriptはアプリ(.app)をログイン項目に加える方式で、起動引数では見分けられません。 - Linux: AppImage で動かしているときは AppImage ファイルのパスが登録されます。ファイルを移動すると起動しなくなるので、置き場所を変えたらオンにし直します。
- iOS / Android: 非対応です。モバイル向けにもビルドするなら、プラグインを使う
useやコマンドも#[cfg(desktop)]で囲みます。 - 共通: 登録名は既定で
productName(無ければ Cargo のパッケージ名)です。後から変えると古い名前の登録が残り、isEnabled()はfalseを返すので、名前は途中で変えない方が無難です。
