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/) for things like HTTP clients, database drivers, etc.
5//!
6//! ## Setting up the Runtime
7//!
8//! `#[tokio::main]` is **completely discouraged** because it messes the event loop, instead create a Tokio runtime in `main()`
9//! and enter it before launching Freya.
10//! The [`enter()`](https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html#method.enter)
11//! guard makes Tokio APIs available on the current thread.
12//!
13//! ```rust,no_run
14//! # use freya::prelude::*;
15//! use tokio::runtime::Builder;
16//!
17//! fn main() {
18//! let rt = Builder::new_multi_thread().enable_all().build().unwrap();
19//! // Enter the Tokio runtime so its APIs (channels, timers, etc.) work.
20//! let _rt = rt.enter();
21//!
22//! launch(LaunchConfig::new().with_window(WindowConfig::new(app)))
23//! }
24//! # fn app() -> impl IntoElement {
25//! # rect()
26//! # }
27//! ```
28//!
29//! The `_rt` guard must remain alive for the duration of the program.
30//! Binding it to a variable in `main()` achieves this.
31//!
32//! ## What you can use
33//!
34//! With the Tokio context active, you can use:
35//!
36//! - **`tokio::sync`** channels (`mpsc`, `watch`, `broadcast`, `oneshot`, etc.)
37//! - **`tokio::time`** utilities (`sleep`, `interval`, `timeout`)
38//! - Any ecosystem crate that requires a Tokio runtime (e.g. `reqwest`, `sqlx`, `tonic`)
39//!
40//! These work well with Freya's built-in `spawn()`:
41//!
42//! ```rust,no_run
43//! # use freya::prelude::*;
44//! # fn app() -> impl IntoElement {
45//! let mut count = use_state(|| 0);
46//!
47//! use_future(move || {
48//! async move {
49//! // tokio::time::sleep works because the Tokio context is active
50//! tokio::time::sleep(std::time::Duration::from_secs(2)).await;
51//! count.set(42);
52//! }
53//! });
54//! # rect()
55//! # }
56//! ```
57//!
58//! ## Caveat about `tokio::spawn`
59//!
60//! Freya's reactivity model is single-threaded, which means you cannot update
61//! e.g state from another thread, and thus using `tokio::spawn` is simply
62//! not possible due to the `Sync` and `Send` bounds.
63//!
64//! You may `tokio::spawn` for background work that does not interact with
65//! components. For anything that updates the UI, use
66//! Freya's [`spawn()`](freya_core::prelude::spawn) instead.
67//!
68//! ## Watch Channels with `use_track_watcher`
69//!
70//! You can easily subscribe to tokio watch channels using [`use_track_watcher`](crate::sdk::use_track_watcher)
71//! hook from the `sdk` feature:
72//!
73//! ```toml
74//! [dependencies]
75//! freya = { version = "...", features = ["sdk"] }
76//! tokio = { version = "1", features = ["sync"] }
77//! ```
78//!
79//! The sender can live on any thread/task. `use_track_watcher` re-renders the
80//! component whenever the watch value changes:
81//!
82//! ```rust,no_run
83//! # use freya::prelude::*;
84//! # use freya::sdk::use_track_watcher;
85//! # use tokio::{runtime::Builder, sync::watch};
86//! # use std::time::Duration;
87//! fn main() {
88//! let rt = Builder::new_multi_thread().enable_all().build().unwrap();
89//! let _rt = rt.enter();
90//!
91//! let (tx, rx) = watch::channel(0);
92//!
93//! launch(
94//! LaunchConfig::new()
95//! .with_future(move |_| async move {
96//! let mut interval = tokio::time::interval(Duration::from_secs(1));
97//! interval.tick().await;
98//! let mut i = 0;
99//! loop {
100//! interval.tick().await;
101//! i += 1;
102//! let _ = tx.send(i);
103//! }
104//! })
105//! .with_window(WindowConfig::new_app(MyApp { rx })),
106//! )
107//! }
108//!
109//! struct MyApp {
110//! rx: watch::Receiver<i32>,
111//! }
112//!
113//! impl App for MyApp {
114//! fn render(&self) -> impl IntoElement {
115//! // Re-renders this component whenever the watched value changes.
116//! use_track_watcher(&self.rx);
117//!
118//! rect()
119//! .expanded()
120//! .center()
121//! .child(format!("Latest value is {}", *self.rx.borrow()))
122//! }
123//! }
124//! ```
125//!
126//! ## Examples
127//!
128//! - [`integration_tokio_spawn.rs`](https://github.com/marc2332/freya/tree/main/examples/integration_tokio_spawn.rs) : Minimal Tokio runtime setup
129//! - [`integration_tokio_watch.rs`](https://github.com/marc2332/freya/tree/main/examples/integration_tokio_watch.rs) : Reactive watch channel with `use_track_watcher`