Last active
August 22, 2025 02:17
-
-
Save lucasshiva/d4e8bd1d123bf552052ab70920f594ac to your computer and use it in GitHub Desktop.
Managing sidecar lifecycle in Tauri 2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::net::TcpListener; | |
use std::sync::{Arc, Mutex}; | |
use tauri::Manager; | |
use tauri_plugin_shell::ShellExt; | |
fn get_available_port() -> Result<u16, std::io::Error> { | |
let listener = TcpListener::bind("127.0.0.1:0")?; | |
let port = listener.local_addr()?.port(); | |
Ok(port) | |
} | |
#[derive(Default)] | |
struct AppState { | |
port: Option<u16>, | |
} | |
#[tauri::command] | |
// Expose the port to the frontend. | |
fn get_service_port(state: tauri::State<'_, Mutex<AppState>>) -> Option<u16> { | |
let state = state.lock().unwrap(); | |
state.port | |
} | |
#[cfg_attr(mobile, tauri::mobile_entry_point)] | |
pub fn run() { | |
tauri::Builder::default() | |
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { | |
// Focus the current window when trying to open a second instance. | |
let _ = app | |
.get_webview_window("main") | |
.expect("No main window") | |
.set_focus(); | |
})) | |
.plugin(tauri_plugin_shell::init()) | |
.plugin(tauri_plugin_dialog::init()) | |
.plugin(tauri_plugin_fs::init()) | |
.plugin(tauri_plugin_opener::init()) | |
.manage(Mutex::new(AppState::default())) | |
.setup(|app| { | |
let port = get_available_port().expect("Failed to get available port"); | |
let sidecar_command = app | |
.shell() | |
.sidecar("signalr_service")? | |
.arg(port.to_string()); | |
match sidecar_command.spawn() { | |
Err(e) => { | |
eprintln!("Failed to spawn sidecar: {}", e); | |
} | |
Ok((_rx, child)) => { | |
let child = Arc::new(Mutex::new(Some(child))); | |
let child_clone = Arc::clone(&child); | |
// We only save the port if spawn succeded. | |
// We could also check if the service is actually running. | |
let state = app.state::<Mutex<AppState>>(); | |
let mut state = state.lock().unwrap(); | |
state.port = Some(port); | |
// Kill service on window close | |
if let Some(window) = app.get_webview_window("main") { | |
window.on_window_event(move |event| { | |
if let tauri::WindowEvent::CloseRequested { .. } = event { | |
let mut child_lock = child_clone.lock().unwrap(); | |
if let Some(child_process) = child_lock.take() { | |
if let Err(e) = child_process.kill() { | |
eprintln!("Failed to kill sidecar process: {}", e); | |
} else { | |
println!("Sidecar process terminated succesfully"); | |
} | |
} | |
} | |
}); | |
} | |
} | |
} | |
Ok(()) | |
}) | |
.invoke_handler(tauri::generate_handler![get_service_port]) | |
.run(tauri::generate_context!()) | |
.expect("error while running tauri application"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment