Custom Task using perform
In this example, we will create an app that fetches your current IP address by making an API call with our own custom task.
Dependencies
As you see, we have two dependencies in our project.
One of them is reqwest. We use reqwest to make the API call.
The other one is iced.
Since this is a guide for iced, that should not wonder you.
But as you see, we added the tokio
feature.
This lets iced use tokio as part of the runtime as needed for reqwest.
[dependencies]
iced = {version="0.13.1", features = ["tokio"]}
reqwest = "0.11.24"
Making the api request
At first, we define what our task will do.
For that, we are creating an async function that makes an async get request to an API that provides the public IP.
use iced::Task;
#[derive(Debug, Clone)]
enum Message {
Refetch,
CurrentIp(String),
}
struct App {
ip: String,
}
impl App {
fn new() -> (Self, iced::Task<Message>) {
(App { ip: String::new() }, Task::none())
}
fn view(&self) -> iced::Element<Message> {
iced::widget::column![
iced::widget::text(&self.ip),
iced::widget::button("Start task").on_press(Message::Refetch)
]
.into()
}
fn update(&mut self, message: Message) -> iced::Task<Message> {
println!("update");
match message {
Message::Refetch => return Task::perform(fetch_ip(), Message::CurrentIp),
Message::CurrentIp(text) => {
self.ip = text;
}
}
Task::none()
}
}
async fn fetch_ip() -> String {
println!("fetch_ip");
reqwest::get("https://api.seeip.org")
.await
.unwrap()
.text()
.await
.unwrap()
}
fn main() {
iced::run("Custom Task Example", App::update, App::view).unwrap();
}
Tip: If you have something that is not async but synchronous and will block your application like a heavy computation, you can use
tokio::spawn_blocking
in a task or subscription to run a closure on a thread where blocking is acceptable.
Starting/Creating the task
In the update function we return Task::none()
or our custom task depending on the message.
If the Message is Message::CurrentIp
we change our state, if it is Message::Refetch
we return our task.
use iced::Task;
#[derive(Debug, Clone)]
enum Message {
Refetch,
CurrentIp(String),
}
struct App {
ip: String,
}
impl App {
fn new() -> (Self, iced::Task<Message>) {
(App { ip: String::new() }, Task::none())
}
fn view(&self) -> iced::Element<Message> {
iced::widget::column![
iced::widget::text(&self.ip),
iced::widget::button("Start task").on_press(Message::Refetch)
]
.into()
}
fn update(&mut self, message: Message) -> iced::Task<Message> {
println!("update");
match message {
Message::Refetch => return Task::perform(fetch_ip(), Message::CurrentIp),
Message::CurrentIp(text) => {
self.ip = text;
}
}
Task::none()
}
}
async fn fetch_ip() -> String {
println!("fetch_ip");
reqwest::get("https://api.seeip.org")
.await
.unwrap()
.text()
.await
.unwrap()
}
fn main() {
iced::run("Custom Task Example", App::update, App::view).unwrap();
}
To create our custom task, we use the Task::perform
function.
It takes a future, in this case our fetch_ip
function, and a closure that converts the returned value of the future into a massage.
use iced::Task;
#[derive(Debug, Clone)]
enum Message {
Refetch,
CurrentIp(String),
}
struct App {
ip: String,
}
impl App {
fn new() -> (Self, iced::Task<Message>) {
(App { ip: String::new() }, Task::none())
}
fn view(&self) -> iced::Element<Message> {
iced::widget::column![
iced::widget::text(&self.ip),
iced::widget::button("Start task").on_press(Message::Refetch)
]
.into()
}
fn update(&mut self, message: Message) -> iced::Task<Message> {
println!("update");
match message {
Message::Refetch => return Task::perform(fetch_ip(), Message::CurrentIp),
Message::CurrentIp(text) => {
self.ip = text;
}
}
Task::none()
}
}
async fn fetch_ip() -> String {
println!("fetch_ip");
reqwest::get("https://api.seeip.org")
.await
.unwrap()
.text()
.await
.unwrap()
}
fn main() {
iced::run("Custom Task Example", App::update, App::view).unwrap();
}
Note:
fetch_ip()
produces the future
Note:
Message::CurrentIp
is a shorthand for|x| Message::CurrentIp(x)
Full Code
use iced::Task;
#[derive(Debug, Clone)]
enum Message {
Refetch,
CurrentIp(String),
}
struct App {
ip: String,
}
impl App {
fn new() -> (Self, iced::Task<Message>) {
(App { ip: String::new() }, Task::none())
}
fn view(&self) -> iced::Element<Message> {
iced::widget::column![
iced::widget::text(&self.ip),
iced::widget::button("Start task").on_press(Message::Refetch)
]
.into()
}
fn update(&mut self, message: Message) -> iced::Task<Message> {
println!("update");
match message {
Message::Refetch => return Task::perform(fetch_ip(), Message::CurrentIp),
Message::CurrentIp(text) => {
self.ip = text;
}
}
Task::none()
}
}
async fn fetch_ip() -> String {
println!("fetch_ip");
reqwest::get("https://api.seeip.org")
.await
.unwrap()
.text()
.await
.unwrap()
}
fn main() {
iced::run("Custom Task Example", App::update, App::view).unwrap();
}