freya/_docs/tokio_integration.rs
1//! # Tokio Integration
2//!
3//! Freya has its own async runtime, but many Rust ecosystem crates depend on
4//! [Tokio](https://tokio.rs/): HTTP clients, database drivers, gRPC libraries, and more.
5//! This page explains how to set up a Tokio runtime alongside Freya so you can use
6//! those crates in your application.
7//!
8//! ## Setting up the Runtime
9//!
10//! Create a Tokio runtime in `main()` and enter its context before launching Freya.
11//! The [`enter()`](https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html#method.enter)
12//! guard makes Tokio APIs available on the current thread without
13//! requiring `#[tokio::main]`:
14//!
15//! ```rust,no_run
16//! # use freya::prelude::*;
17//! use tokio::runtime::Builder;
18//!
19//! fn main() {
20//! let rt = Builder::new_multi_thread().enable_all().build().unwrap();
21//! // Enter the Tokio context so its APIs (channels, timers, etc.) work.
22//! let _rt = rt.enter();
23//!
24//! launch(LaunchConfig::new().with_window(WindowConfig::new(app)))
25//! }
26//!
27//! fn app() -> impl IntoElement {
28//! rect()
29//! }
30//! ```
31//!
32//! The `_rt` guard must remain alive for the duration of the program.
33//! Binding it to a variable in `main()` achieves this.
34//!
35//! ## What you can use
36//!
37//! With the Tokio context active, you can use:
38//!
39//! - **`tokio::sync`** channels (`mpsc`, `watch`, `broadcast`, `oneshot`, etc.)
40//! - **`tokio::time`** utilities (`sleep`, `interval`, `timeout`)
41//! - Any ecosystem crate that requires a Tokio runtime (e.g. `reqwest`, `sqlx`, `tonic`)
42//!
43//! These work well with Freya's built-in `spawn()`:
44//!
45//! ```rust,no_run
46//! # use freya::prelude::*;
47//! # fn app() -> impl IntoElement {
48//! let mut count = use_state(|| 0);
49//!
50//! use_hook(move || {
51//! spawn(async move {
52//! // tokio::time::sleep works because the Tokio context is active
53//! tokio::time::sleep(std::time::Duration::from_secs(2)).await;
54//! count.set(42);
55//! });
56//! });
57//! # rect()
58//! # }
59//! ```
60//!
61//! ## Caveat about `tokio::spawn`
62//!
63//! `tokio::spawn` runs tasks on the **Tokio** runtime, which is separate from
64//! Freya's reactivity system. Tasks spawned there **cannot directly update
65//! component state**. Signals, hooks, and other reactive primitives are
66//! tied to Freya's own executor.
67//!
68//! Use `tokio::spawn` only for background work that does not interact with
69//! components (e.g. writing to a file, sending a network request whose
70//! result you don't need in the UI). For anything that updates the UI, use
71//! Freya's [`spawn()`](freya_core::prelude::spawn) instead.
72//!
73//! ## Watch Channels with `use_track_watcher`
74//!
75//! You can push data **into** Freya from an external source
76//! (a background thread, another runtime, a hardware driver, etc.).
77//! `tokio::sync::watch` channels pair well with the
78//! [`use_track_watcher`](crate::sdk::use_track_watcher) hook from the `sdk`
79//! feature:
80//!
81//! ```toml
82//! [dependencies]
83//! freya = { version = "...", features = ["sdk"] }
84//! tokio = { version = "1", features = ["sync"] }
85//! ```
86//!
87//! The sender can live on any thread. `use_track_watcher` re-renders the
88//! component whenever the watch value changes:
89//!
90//! ```rust,ignore
91//! # use freya::prelude::*;
92//! # use freya::sdk::use_track_watcher;
93//! # use tokio::sync::watch;
94//! # use std::time::Duration;
95//! fn main() {
96//! let (tx, rx) = watch::channel(0);
97//!
98//! // Producer: a plain thread that pushes new values.
99//! std::thread::spawn(move || {
100//! let mut i = 0;
101//! loop {
102//! std::thread::sleep(Duration::from_secs(1));
103//! i += 1;
104//! let _ = tx.send(i);
105//! }
106//! });
107//!
108//! launch(LaunchConfig::new().with_window(WindowConfig::new_app(MyApp { rx })))
109//! }
110//!
111//! struct MyApp {
112//! rx: watch::Receiver<i32>,
113//! }
114//!
115//! impl App for MyApp {
116//! fn render(&self) -> impl IntoElement {
117//! // Re-renders this component whenever the watch value changes.
118//! use_track_watcher(&self.rx);
119//!
120//! rect()
121//! .expanded()
122//! .center()
123//! .child(format!("Latest value is {}", *self.rx.borrow()))
124//! }
125//! }
126//! ```
127//!
128//! ## Examples
129//!
130//! - [`integration_tokio.rs`](https://github.com/marc2332/freya/tree/main/examples/integration_tokio.rs) : Minimal Tokio runtime setup
131//! - [`sdk_watch.rs`](https://github.com/marc2332/freya/tree/main/examples/sdk_watch.rs) : Reactive watch channel with `use_track_watcher`