# services Background services. A service owns a route key, runs as an iced `Subscription`, receives `common::Wire`s on its `Inbox`, and publishes payloads back to every module subscribed to its key. Core maps a `Service` route key to a subscription through `IntoSubscription`. Message types are shared, so they live in the `common` crate, under `common/src/messages/services/`. That is what lets a service and the modules that consume it name the same payload without either depending on the other. ## Getting started: add a service 1. Declare the service's route key and payload in `crates/common/src/messages/services/greeter.rs`: ```rust //! Greeter service messages. use crate::Service; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum GreeterKind { Hello, } impl GreeterKind { /// Route key a module subscribes under to receive this kind's ticks. pub fn key(self) -> Service { Service(match self { Self::Hello => "greeter.hello", }) } } #[derive(Clone)] pub struct GreeterPayload { pub value: String, } ``` 2. Re-export them from `crates/common/src/messages/services/mod.rs`: ```rust mod datetime; mod greeter; mod weather; pub use datetime::{ClockKind, ClockPayload}; pub use greeter::{GreeterKind, GreeterPayload}; pub use weather::*; ``` 3. Create `crates/services/src/greeter.rs`: ```rust use std::time::Duration; use common::{Endpoint, Inbox, Wire, GreeterPayload}; use iced::{futures::channel::mpsc, Subscription}; /// Route-key namespace owned by this service; keys look like `"greeter.hello"`. pub const NAMESPACE: &str = "greeter."; pub struct GreeterService; impl GreeterService { pub fn run(inbox: Inbox) -> Subscription { Subscription::run_with(inbox, |inbox| { let key = inbox.key(); let mut _rx = inbox.take().expect("inbox receiver is taken once"); iced::stream::channel(0, move |mut sender: mpsc::Sender| async move { let mut interval = tokio::time::interval(Duration::from_secs(5)); loop { interval.tick().await; // Fan-out: publish to every module subscribed to `key`. let wire = Wire::topic(Endpoint::service(key), key, GreeterPayload { value: "hello".into(), }); if sender.send(wire).await.is_err() { return; } } }) }) } } ``` 4. Add one arm to the `IntoSubscription` match in `crates/services/src/lib.rs`: ```rust pub mod greeter; impl IntoSubscription for Service { fn into_subscription(self, inbox: Inbox) -> Subscription { match self.0 { key if key.starts_with(datetime::NAMESPACE) => datetime::ClockTicker::run(inbox), key if key.starts_with(weather::NAMESPACE) => weather::WeatherService::run(inbox), key if key.starts_with(greeter::NAMESPACE) => greeter::GreeterService::run(inbox), _ => Subscription::none(), } } } ``` Then a module subscribes by returning `GreeterKind::Hello.key()` from its `services()` and downcasts `GreeterPayload` in `update()`. `run_with(inbox, ..)` keys the subscription by the inbox route key, so the same subscription returned on every rebuild is not restarted. The receiver is taken once inside the closure — don't call `take()` anywhere else.