Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

websocket example and helpers to create custom subscriptions #1198

Merged
merged 10 commits into from
Jan 17, 2022

Conversation

hecrj
Copy link
Member

@hecrj hecrj commented Jan 17, 2022

This PR introduces a couple of helper functions in iced_native::subscription to help users create custom subscriptions.

Specifically, it introduces both a run and unfold method:

/// Returns a [`Subscription`] that will create and asynchronously run the
/// given [`Stream`].
///
/// The `id` will be used to uniquely identify the [`Subscription`].
pub fn run<I, S, Message>(id: I, stream: S) -> Subscription<Message>
where
    I: Hash + 'static,
    S: Stream<Item = Message> + Send + 'static,
    Message: 'static,
{
    // ...
}

/// Returns a [`Subscription`] that will create and asynchronously run a
/// [`Stream`] that will call the provided closure to produce every `Message`.
///
/// The `id` state will be used to uniquely identify the [`Subscription`].
pub fn unfold<I, T, Fut, Message>(
    id: I,
    initial: T,
    mut f: impl FnMut(T) -> Fut + Send + Sync + 'static,
) -> Subscription<Message>
where
    I: Hash + 'static,
    T: Send + 'static,
    Fut: Future<Output = (Option<Message>, T)> + Send + 'static,
    Message: 'static + Send,
{
    // ...
}

The unfold helper can be leveraged to create a Subscription that spawns an asynchronous worker in the background and establish a channel of communication with an iced application.

This can be achieved by creating an mpsc channel inside the closure and returning the Sender as a Message for the Application:

use iced_native::subscription::{self, Subscription};
use iced_native::futures::channel::mpsc;

pub enum Event {
    Ready(mpsc::Sender<Input>),
    WorkFinished,
    // ...
}

enum Input {
    DoSomeWork,
    // ...
}

enum State {
    Starting,
    Ready(mpsc::Receiver<Input>),
}

fn some_worker() -> Subscription<Event> {
    struct SomeWorker;

    subscription::unfold(std::any::TypeId::of::<SomeWorker>(), State::Starting, |state| async move {
        match state {
            State::Starting => {
                // Create channel
                let (sender, receiver) = mpsc::channel(100);

                (Some(Event::Ready(sender)), State::Ready(receiver))
            }
            State::Ready(mut receiver) => {
                use iced_native::futures::StreamExt;

                // Read next input sent from `Application`
                let input = receiver.select_next_some().await;

                match input {
                    Input::DoSomeWork => {
                        // Do some async work...

                        // Finally, we can optionally return a message to tell the
                        // `Application` the work is done
                        (Some(Event::WorkFinished), State::Ready(receiver))
                    }
                }
            }
        }
    })
}

As usual, I have implemented a new example to showcase this functionality. The new websocket example uses the subscription::unfold helper to open and maintain a WebSocket connection to an echo server. The Application can send and receive messages from the WebSocket managed by the Subscription.

I hope this makes implementing custom subscriptions considerably easier for everyone!

Closes #1172.
Closes #405.

@hecrj hecrj added the feature New feature or request label Jan 17, 2022
@hecrj hecrj added this to the 0.4.0 milestone Jan 17, 2022
@hecrj hecrj merged commit 92a699b into master Jan 17, 2022
@hecrj hecrj deleted the subscription-helpers branch January 17, 2022 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Is it possible to send a Message manually? How to inject custom events into application?
1 participant