notifications
Build a notification, schedule it, cancel it.
[dependencies]
notifications = "*"
permissions = "*" # this package's own dependency, restated — see below
stdlib = "*"
[macos.dependencies]
objc = "*"
[ios.dependencies]
objc = "*"
[android.dependencies]
jni = "*"
android_view = "*"
facet = "*"
flex_layout = "*"
events = "*"
The resolver validates every import against ONE flat set taken from your manifest — it does not read a dependency's own — so a package's transitive deps are named again here. Miss one and the link says which symbol.
import "notifications/notifications" as notifications;
Common case
let o: notifications::Outcome = notifications::schedule(
notifications::Notification::new("reminder:1", "Stand up",
body: "You have been sitting for an hour.",
when: notifications::When::After(3600.0f64)));
if o == notifications::Outcome::NotPermitted {
// Ask, then schedule again. This package refuses rather than handing the
// platform a notification it will accept and never show.
}
Things that will bite you
- Ask for the permission first on Apple and Android. Those platforms accept
a notification from an app without permission and silently never show it.
This package returns
NotPermittedinstead — which is why it depends onpermissions. - On Apple, nothing appears while your app is in front unless a delegate says to present it. This package installs one. Without it the code is correct and nothing happens, which is the most common "notifications don't work" report there is.
- On macOS a Focus mode silences your app along with everyone else's. The
notification lands in Notification Centre with no banner, which is the same
symptom as a broken one.
tools/why_quiet.sh <bundle-id>reads macOS's decision back out of the system log before you touch the code. - Android needs one manifest line for action buttons:
<receiver android:name="cplus.facet.FacetNotificationReceiver" android:exported="false" />. Without it buttons quietly do nothing. - Register
on_tapwhenever you like. A tap that launched the app fired before anything subscribed; the latch replays it at registration. - On Android a missing channel drops the notification silently. This package
creates one before every post, so you never meet it — but it is why
channelis never passed through unchecked.
Docs
| Need | File |
|---|---|
| Use it in minutes | docs/tutorial.md |
| How / why / gotchas | docs/guide.md |
| Exact signatures | docs/ref.md |
Platforms
| Platform | Local scheduling | Notes |
|---|---|---|
| iOS | yes | UNUserNotificationCenter; needs a bundle |
| macOS | yes | same file as iOS; needs a bundle, and the demo shows why |
| Android | yes | channels, NotificationManager; deferred does not survive process death |
| Windows | yes | Shell_NotifyIconW balloons — real toasts on Win10/11, no bundle or AUMID needed. Deferred does not survive process death. No action buttons |
| Linux | Unsupported |
parked — the D-Bus spec has no scheduling concept |
Not here: push tokens (an entitlement and a provisioning profile on Apple,
the Firebase pipeline on Android — a remote notification that arrives is
handled exactly like a local one), and media/transport-control notifications
(sticky + actions covers the ask; the rest comes from a MediaSession).
See plans/notifications.md §6 and §9.
Taps work cold and warm on Apple and Android. on_tap hands back the
payload the notification carried. A tap on a dead app launches it and still
reaches your handler — facet/app_events latches the payload, so registering
late is safe. The current Windows balloon backend posts and clears
notifications but does not receive clicks; Linux is unsupported.
Tests
cd vendor/notifications && cpc test # 174 checks, macOS host
vendor/notifications/tools/run_ios_tests.sh # 9 checks, iOS simulator
The host suite covers the arithmetic, the guards and the record. It cannot
reach the framework: cpc test builds a bare binary, and
UNUserNotificationCenter refuses a process with no .app bundle — so the
iOS runner is a bundled app, which is the only configuration where the centre
exists. examples/notifications_demo is the macOS one, and it is where a person
sees a notification actually arrive.