Saltar al contenido

Comunicación entre ventanas

Dos ventanas no se hablan: hablan con el core. Los tres patrones —el evento dirigido, el estado compartido y el difundido— y por qué el core como único árbitro es la respuesta correcta y no un rodeo.

Por qué el core es el árbitro

Dos ventanas son dos webviews, y dos webviews son dos contextos aislados por el sistema operativo —la propiedad del nivel 4.3—. No hay memoria compartida, no hay variables comunes, y el único sitio con acceso a las dos es el proceso que las creó.

Que el canal directo no exista fuerza una propiedad valiosa: el estado compartido vive en un solo sitio y ese sitio es el core. Una arquitectura donde dos ventanas se sincronizan entre sí tiene dos copias de la verdad y necesita una regla para resolver los desacuerdos; con el core como árbitro, no hay desacuerdo posible.

Los tres patrones

1 · El evento dirigido. El core avisa a una ventana concreta, por su etiqueta.

Rust
// Desde un comando invocado por la ventana de ajustes:app.emit_to("main", "preferencia-cambiada", &nueva)?;

Es el más directo y sirve cuando sabes exactamente a quién le importa. El nivel 20 entra en el sistema de eventos completo; aquí basta con saber que el destino se nombra por etiqueta.

2 · El estado compartido en el core. El dato vive en el core y las ventanas lo piden.

Rust
// El estado es del programa, no de ninguna ventana. Nivel 22.#[tauri::command]async fn set_tema(state: tauri::State<'_, Ajustes>, tema: String) -> Result<(), ErrorPayload> {    state.set_tema(&tema)?;      // se guarda donde debe    Ok(())}

Es el patrón correcto para cualquier cosa que no sea de una ventana concreta: las preferencias, el proyecto abierto, la sesión. Y se combina con el primero: el comando guarda y después emite para que quien lo esté mostrando se entere.

3 · La difusión. El core avisa a todas.

Rust
app.emit("catalogo-actualizado", &resumen)?;   // a todas las ventanas

Sirve cuando el cambio afecta a cualquiera que esté escuchando y no quieres llevar la cuenta de quién hay abierto. Es cómodo y tiene un coste: cada ventana recibe el evento aunque no le sirva, lo que en una aplicación con muchas ventanas y eventos frecuentes se nota.

Cuál elegir

La última fila es un recordatorio útil: no todo estado es compartido. Meter en el core lo que solo le importa a una vista añade cruces del puente y complejidad sin ganar nada.

El error clásico

Es este, y aparece siempre igual: la ventana principal guarda el estado en su JavaScript, la ventana de ajustes le pide el valor por un evento, y la principal se lo contesta por otro.

Funciona, y tiene tres problemas que se manifiestan en orden:

  1. Si la ventana principal está cerrada, no hay a quién preguntar. Y una ventana de ajustes que solo funciona con la principal abierta es una limitación que nadie pidió.
  2. El estado se pierde al recargar. Un recargado de la webview —que en desarrollo pasa a cada rato— borra la verdad de la aplicación.
  3. Hay dos copias en cuanto la segunda ventana guarda la suya, y toca escribir la lógica de reconciliación que el core habría hecho innecesaria.

La corrección es directa y es la del nivel 4.1: si tiene consecuencias fuera de una pantalla, va en el core. Una preferencia que afecta a dos ventanas es, por definición, de las que tienen consecuencias fuera de una pantalla.