-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
How to decouple view logic from update logic? #327
Comments
Thank you for your fast response. It seems that indeed those examples suit my needs. |
@hecrj Is it possible to also have some sort of subscription but for the view state? I guess this is somewhat related to #241. |
@sum-elier Yes, that's indeed how it works. The messages produced by a Check out the |
Yes, that is correct, and I've been using those great examples you have 😃 ! But... what I see is the subscription generates events to be consumed by As an example of what I have and what I am trying to do, if that makes sense (pseudo code): #[derive(Clone, PartialEq, Eq, Debug)]
pub enum Message {
FieldA(String),
FieldB(String),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct State {
pub field_a: String,
pub field_b: String,
}
pub struct BusinessLogic<'a> {
external_events: BoxStream<'a, String>,
state: MyEventPublisher<State>,
}
impl<'a> BusinessLogic<'a> {
pub fn new(external_events: BoxStream<'a, String>) -> BusinessLogic<'a> {
BusinessLogic {
external_events,
state: MyEventPublisher::new(State {
field_a: "".to_string(),
field_b: "".to_string(),
}),
}
}
pub fn update(&mut self, msg: Message) {
match msg {
Message::FieldA(field_a) => {
self.update_field_a(new_field_a);
self.emit_new_state();
}
Message::FieldB(new_field_b) => {
self.update_field_b(new_field_b);
self.emit_new_state();
}
};
}
pub fn state_changes<'a>(&self) -> BoxStream<'a, State> {
let states = self.state.to_stream().boxed();
self.external_events
.join(states)
.map(|(state, external_event)| combining_func(state, external_event))
}
} So I would like to subscribe to the stream returned by The purpose is to ideally have a fully reactive application using streams which works independently of the chosen UI framework. |
It feels like you are trying to implement your own MVC architecture on top of Instead, process any state changes in There is no need to emit any events from |
Well not really, at least not intentionally. What I wanted was to have the state stored in streams instead of mutable variables that can be read outside these streams, more like FRP. So the update method receives messages, and pushes them into the streams which contain all the business logic and at the end there's a single output from a final stream with the view state. At the end I wanted to use from Iced only the GUI part of it since it knows how to render widgets given some data (state), independently of how my UI architecture is implemented, thus ignoring the lifecycle, command execution and so on. Maybe this is related to #313 |
I don't think there is anything in particular stopping you from doing what you want. Just keep in mind that this is not the correct way to think about it:
Your whole You can store your logic in different streams, notify your "final view" (the |
@hecrj Thanks for answering back, really appreciated. Sorry for the insistence but I love this project and Rust and just trying to make it work for me. Maybe the problem is that I am a rookie at Rust. But what I want to achieve is have a completely modular stream oriented application which can reuse any kind of GUI library or framework with ease without depending on a specific paradigm. My view state was represented inside a stream à la FRP which emitted events every time a message was processed in the update method of one of these modules. Afterwards I wanted to map it as you say to some visual representation, but since Iced doesn't work that way, that is: using a stream of view states (like push-based), but rather it reads from a view state (like poll-based), then I decided to simply store the view state in the module and have a method that reads the view state called from the framework's But now I have encountered a related (maybe?) problem while trying to have a subscription from a struct that also serves as the one that does the updates. I am unable to create a subscription that works as presented. #[derive(Clone, PartialEq, Eq, Debug)]
pub enum LogicMessage {
Value,
}
pub struct Logic;
impl Logic {
pub fn update(&mut self, lmsg: LogicMessage) {}
pub fn stream(&self) -> BoxStream<LogicMessage> {
self.other_stream(self.result_from_update).boxed()
}
}
pub struct View;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Message {
FromLogic(LogicMessage),
}
pub struct MyApp {
logic_and_view: LogicAndView,
}
pub struct LogicAndView {
pub logic: Logic,
pub view: View,
}
impl LogicAndView {
pub fn new(logic: Logic) -> Self {
Self {
logic,
view: View {},
}
}
}
impl Application for MyApp {
type Executor = iced_futures::executor::AsyncStd;
type Flags = ();
type Message = Message;
fn new(_flags: ()) -> (Self, Command<Message>) {
let logic_and_view = LogicAndView::new(Logic {});
(Self { logic_and_view }, Command::none())
}
fn title(&self) -> String {
String::new()
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::FromLogic(logic_msg) => {
&mut self.logic_and_view.logic.update(logic_msg);
}
}
Command::none()
}
fn view(&mut self) -> Element<Message> {
self.logic_and_view
.view
.set_view(self.logic_and_view.logic.get_view_data())
.map(Message::FromLogic)
}
fn subscription(&self) -> Subscription<Message> {
self.logic_and_view.logic
.stream()
./*somehow create a subscription that compiles*/
.map(Message::FromLogic)
}
} |
First of all this an awesome project, thank you for publishing this.
I was wondering if it is possible to decouple the view logic from the update logic? Currently the update logic is only invoked in response to a message from the view logic, but if I were to implement some business logic which reads continuously from a TCP socket and pushes that to the UI, how could I implement this without having to send a dummy message from the view logic every time an event is consumed from the TCP socket to keep the stream going on?
Ideally I would like to drive the UI from the business logic, and if some user interaction happens it is handled as usual, but without having to drive the business logic necessarily from UI events.
The text was updated successfully, but these errors were encountered: