setup: qué va ahí y qué no
El gancho donde la aplicación ya existe y el bucle todavía no ha empezado. Qué se puede hacer, qué se debe, y el criterio que impide que el arranque se llene de trabajo.
Qué existe
La tabla del nivel 4.2, ahora con detalle:
El criterio
Una sola pregunta: ¿tiene que estar listo antes de la primera interacción del usuario?
Sacar el trabajo sin perder el orden
Lo que se saca sigue teniendo que ocurrir, y a veces en cierto orden. El patrón:
.setup(|app| { // lo imprescindible, síncrono y rápido let db = Db::abrir(&app.path().app_data_dir()?)?; app.manage(db); // lo demás, en una tarea, con su orden dentro let handle = app.handle().clone(); tauri::async_runtime::spawn(async move { migrar_si_hace_falta(&handle).await; // primero precalentar_cache(&handle).await; // después comprobar_actualizaciones(&handle).await; // y al final, lo menos urgente let _ = handle.emit(eventos::LISTO, ()); }); Ok(())})Dos cosas de ese fragmento: el orden se conserva dentro de la tarea, así que sacar trabajo no significa perder secuencia; y el evento final permite que la interfaz sepa cuándo todo está caliente, con la salvedad de la carrera del nivel 20.3 —hace falta también un comando que devuelva el estado—.
Cuando la inicialización falla
El gancho devuelve un resultado, y un error ahí impide que la aplicación arranque. Eso está bien para lo que de verdad es imprescindible —si la base de datos no abre, no hay aplicación— y produce una experiencia mala: el proceso muere sin ventana y sin explicación, y el usuario solo ve que no pasa nada.
La forma decente de tratarlo son tres pasos:
- Registrar el error completo, con contexto. Es lo único que vas a tener.
- Enseñar algo. Un diálogo nativo del sistema con el mensaje, que no necesita ventana ni webview y funciona aunque el frontend no exista todavía.
- Y salir, o arrancar en un modo degradado si tiene sentido.
Lo que no vale es devolver el error a secas: un proceso que muere en silencio es indistinguible de un instalador roto.