I have an iced application that I want to control through a web UI. For this I use axum and spawn it in a different tokio task. I would like to send a message from the axum endpoint to the iced application, but I can neither invoke the update method of the iced application nor can I listen to the crossbeam channel inside the iced application.
My code looks like this right now:
mod messages;
mod ui;
mod web;
use crossbeam_channel::unbounded;
use iced::Application;
use iced::Settings;
use tokio;
#[tokio::main]
async fn main() -> iced::Result {
let (s, r) = unbounded::<messages::WebToUIMessage>();
let server_handle = tokio::task::spawn(async { web::run_server(s).await });
let result = ui::MediaHub::run(Settings::default());
// We abort the server when the UI finishes
server_handle.abort();
result
}
You can pass a channel receiver through a Flags struct when starting an iced app. Then in the subscription function use iced::subscription::unfold to convert the received channel messages into iced messages that the UI will respond to. Runnable code is as follows:
use iced::{widget::text, Application, Command, Element, Settings, Subscription};
use tokio::sync::mpsc;
use std::cell::RefCell;
fn main() -> iced::Result {
let (sender, receiver) = mpsc::unbounded_channel::<i32>();
std::thread::spawn(move || {
for i in 0.. {
sender.send(i).unwrap();
std::thread::sleep(std::time::Duration::from_millis(200));
}
});
Ui::run(Settings::with_flags(UiFlags { receiver }))
}
struct UiFlags {
receiver: mpsc::UnboundedReceiver<i32>,
}
struct Ui {
receiver: RefCell<Option<mpsc::UnboundedReceiver<i32>>>,
num: i32,
}
#[derive(Debug, Clone)]
enum Message {
ExternalMessageReceived(i32),
}
impl Application for Ui {
type Executor = iced::executor::Default;
type Message = Message;
type Theme = iced::Theme;
type Flags = UiFlags;
fn new(flags: UiFlags) -> (Self, Command<Message>) {
let app = Ui {
receiver: RefCell::new(Some(flags.receiver)),
num: 0,
};
(app, Command::none())
}
fn title(&self) -> String {
String::from("External Message Example")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::ExternalMessageReceived(num) => {
self.num = num;
}
}
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
iced::subscription::unfold(
"led changes",
self.receiver.take(),
move |mut receiver| async move {
let num = receiver.as_mut().unwrap().recv().await.unwrap();
(Some(Message::ExternalMessageReceived(num)), receiver)
},
)
}
fn view(&self) -> Element<Message> {
text(self.num).into()
}
}