Skip to content
LucaCappelletti94Public

About

Push notifications for Rust apps through each platform's own service, APNs on iOS and macOS, FCM or UnifiedPush on Android, WNS on Windows, UnifiedPush on Linux and Web Push in the browser.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

pushups

pushups logo

CI codecov docs.rs crates.io MSRV License: MIT

Push notifications for Rust apps through each platform's own service, APNs on iOS and macOS, FCM or UnifiedPush on Android, WNS on Windows, UnifiedPush on Linux and Web Push in the browser. The app calls install in main before its toolkit starts, sets one handler and calls register. The handler gets the token for the app's server and every push, including the one that started the app.

use pushups::{Config, Event};

if let Err(error) = pushups::install(Config::new()) {
    eprintln!("no push on this target: {error}");
}
pushups::set_handler(|event| match event {
    Event::Token(token) => println!("send {token:?} to the server"),
    Event::RegistrationFailed(error) => eprintln!("no push token: {error}"),
    Event::Message(message) => println!("{} bytes, started the app: {}", message.payload.len(), message.started_app),
    Event::MessagesDropped => println!("fetch what was missed from the server"),
});
let _ = pushups::register();

The handler runs on the platform's delivery thread, so waking the toolkit is its job. The stream feature adds pushups::events(), the same events as a stream. The encryption feature makes the pushes end-to-end encrypted, with the device's keys in the platform key store.

Support

Android iOS macOS Windows Linux Web
Dioxus 0.7 Dioxus on Android
yes. A tap into the running app restarts it unless MainActivity is singleTask, as examples/dioxus sets, and the page can occasionally freeze until dioxus#5916
Dioxus on iOS
yes up to iOS 26, iOS 27 needs dioxus#5893
Dioxus on macOS
yes
Dioxus on Windows
panics when its window cannot take focus, as on a locked screen, until dioxus#5916
Dioxus on Linux
yes
Dioxus on Web
yes1
Tauri 2 Tauri on Android
yes
Tauri on iOS
yes
Tauri on macOS
yes
Tauri on Windows
yes
Tauri on Linux
yes
winit 0.31 winit on Android
needs winit#4752, which examples/winit pins
winit on iOS
yes
winit on macOS
yes
winit on Windows
yes
winit on Linux
yes
winit on Web
yes1
Leptos 0.8 Leptos on Web
yes1
Yew 0.23 Yew on Web
yes1

Dioxus and Tauri run on tao, so their cells cover a tao app. Without the winit fix, a fresh install's first launch can get no FCM token until the app is reopened.

Upstream fixes worth having

PR Why
dioxus#5901 dx builds the iOS app icon from Dioxus.toml, the icon iOS shows on every notification
dioxus#5898 dx bundle --macos fills each icon size from the closest PNG, so notifications show a sharp icon instead of one scaled from the first PNG listed
dioxus#4063, dioxus#5693 dx sets the Android launcher icon, which FCM notifications use when the app names no notification icon
dioxus#5591 dx bundle makes an IPA that Apple's validator accepts, the route from dx to the App Store
dioxus#5900 dx renames its bundled JS when only this crate's JavaScript changes, so a web build stops serving the previous release's copy after an upgrade
tao#1364 tao reports the push token and pushes itself, so a tao app without this crate can receive them

Android

The crate's Gradle module holds the FCM and UnifiedPush services, and its Rust side uses JNI 0.21, as Dioxus and Tauri do. dx bundles the module through the dioxus feature, a Tauri app adds tauri-plugin-pushups, and any other Gradle project depends on io.github.lucacappelletti94:pushups-android at the crate's version or includes android/ through cargo metadata as examples/winit does. A UnifiedPushConfig sends pushes through the device's UnifiedPush distributor when it has one, and Notification::show presents data-message content without an Activity. A push the background handler answers with a notification goes out with android.priority set to high, since FCM may hold a normal-priority message for minutes and demotes high-priority messages that show no notification.

use pushups::{Context, Message};

pushups::firebase_config!("tests/fixtures/google-services.json");

#[pushups::background_handler]
fn on_push(context: Context<'_>, message: Message) {
    let _ = (context, message.payload);
}

iOS and macOS

The crate adds its push methods to whatever app delegate the toolkit installs, while closed-app iOS alert transformation uses a separate [rendering_handler] Rust callback and Swift extension adapter. The bundle needs the push entitlement and, on iOS, the remote-notification background mode, which a dx app sets in Dioxus.toml. A Tauri app gets the iOS entitlement and the background mode from tauri-plugin-pushups and names a macOS entitlements file in tauri.conf.json, as examples/tauri does. With the encryption feature the app and the extension share the keychain access group both targets carry in the keychain-access-groups entitlement, which each Info.plist names to the crate with the PushupsKeychainAccessGroup string key.

[ios.entitlements]
aps-environment = "development"

[macos.entitlements]
"com.apple.developer.aps-environment" = "development"

[background]
remote-notifications = true

Web

The app gives its VAPID public key and serves pushups::SERVICE_WORKER at /pushups-sw.js, which the dioxus feature has dx do and a Trunk build script writes with pushups::write_service_worker, or sets rust_handler to run its own wasm as the worker, as examples/yew and examples/leptos show. On iOS only home-screen web apps get pushes, and they reach the page when it is next shown (WebKit bug 268797).

use pushups::{Config, VapidPublicKey, WebPushConfig};

let key = VapidPublicKey::from_base64url(
    "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4",
)?;
let config = Config::new().web(WebPushConfig::new(key));
# let _ = config;
# Ok::<(), pushups::Error>(())

Token::web_push_subscription gives the endpoint and keys the server sends to, and with the serde feature it serialises as the browser's PushSubscription.toJSON(), which web-push and pywebpush read. UnifiedPush tokens on Android and Linux take the same form.

Linux

The crate speaks UnifiedPush over D-Bus to the user's distributor, such as KDE's, and a push to a closed app starts it through D-Bus activation for the background handler. The desktop shows nothing for a push by itself, so the app's rendering_handler calls Notification::show with the notification it returns, and a tap on a notification with data reaches the app as a message. Through the desktop portal, 1.19.4 or later, a tap on a closed app opens it. Without that portal a tap reaches the app only while the process that showed the notification runs. LinuxConfig::app_notifications names the app and its icon on the notifications, otherwise they carry the app id.

use pushups::{Config, LinuxConfig, UnifiedPushConfig, VapidPublicKey};

let key = VapidPublicKey::from_base64url(
    "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4",
)?;
let config = Config::new()
    .linux(
        LinuxConfig::new("org.example.App")
            .app_notifications("Example", "/usr/share/icons/example.png"),
    )
    .unified_push(UnifiedPushConfig::new(key));
# let _ = config;
# Ok::<(), pushups::Error>(())

Windows

The app's installer puts the Windows App SDK 2.5 runtime on the machine, and the app ships Microsoft.WindowsAppRuntime.Bootstrap.dll beside its executable. It gives the Object ID of its Entra ID app registration's service principal, and a push to a closed app starts it for the background handler. With WnsConfig::app_notifications, naming the app and an icon file, Notification::show puts a toast in the notification centre, and a tap on one with data reaches the app as a message, starting it when it has exited. Windows refuses a toast past about 5 KB, which leaves about 3.5 KB for data under a short title.

use pushups::{Config, WnsConfig};

let config = Config::new().windows(WnsConfig::new("00000000-0000-0000-0000-000000000000"));
# let _ = config;

Alternatives

ALTERNATIVES.md compares the other Rust crates that receive pushes, by framework and system.

Footnotes

  1. On iOS only home-screen web apps get pushes, and they reach the page when it is next shown (WebKit bug 268797). ↩ ↩2 ↩3 ↩4

About

Push notifications for Rust apps through each platform's own service, APNs on iOS and macOS, FCM or UnifiedPush on Android, WNS on Windows, UnifiedPush on Linux and Web Push in the browser.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages