Notifications

Nib provides full access to macOS system notifications, including instant push, scheduled delivery, custom sounds, action buttons, and text input.

Built app only

macOS delivers notifications to app bundles. Under nib run, app.notify() and push() fall back to a basic AppleScript banner without actions or sounds, and scheduling is unavailable. Test notifications with nib build.

Quick Notification

The simplest way to send a notification is app.notify():

import nib

def main(app: nib.App):
    app.title = "Notify"
    app.icon = nib.SFSymbol("bell")
    app.width = 200
    app.height = 100

    app.build(
        nib.Button("Send Notification", action=lambda: app.notify("Hello!", "World"))
    )

nib.run(main)

app.notify() parameters

app.notify(
    "Download Complete",
    body="report.pdf has been saved to Downloads",
    subtitle="File Manager",
    sound=True,
)

Requesting Permission

macOS requires user permission before delivering notifications. Request it early:

def on_permission(granted):
    if granted:
        print("Notifications enabled")
    else:
        print("Notifications denied")

app.notifications.request_permission(callback=on_permission)

Note

The first call shows the system permission dialog. Subsequent calls return the cached result. If the user denied permission, they must re-enable it in System Settings.

Notification Objects

For full control, create Notification objects and push them through the notification manager:

from nib import Notification

notification = Notification(
    title="New Message",
    body="You have a new message from Alice",
    subtitle="Messages",
)

app.notifications.push(notification)

Custom sounds

Use NotificationSound to configure the notification sound:

from nib import Notification, NotificationSound, NotificationSoundName

# Default sound
notification = Notification(
    title="Alert",
    body="Something happened",
    sound=NotificationSound(name=NotificationSoundName.DEFAULT),
)

# Built-in macOS sound by name
notification = Notification(
    title="Alert",
    body="Something happened",
    sound=NotificationSound(name=NotificationSoundName.custom("Glass")),
)

# Other built-in sounds: "Frog", "Ping", "Pop", "Purr", "Sosumi", "Tink"

Scheduling Notifications

Schedule at a specific time

from datetime import datetime, timedelta
from nib import Notification

reminder = Notification(
    title="Break Time",
    body="You have been working for 2 hours. Take a break!",
)

# Schedule for 1 hour from now
notification_id = app.notifications.schedule(
    reminder,
    at=datetime.now() + timedelta(hours=1),
)

Schedule daily recurring

from nib import Notification

daily_reminder = Notification(
    title="Daily Standup",
    body="Time for the morning standup meeting",
)

# Every day at 9:00 AM
app.notifications.schedule_daily(
    daily_reminder,
    from_time="09:00",
)

# Multiple times per day between 8 AM and 6 PM
app.notifications.schedule_daily(
    daily_reminder,
    from_time="08:00",
    to_time="18:00",
    count=3,
)

# Daily for one week
from datetime import date, timedelta

app.notifications.schedule_daily(
    daily_reminder,
    from_time="09:00",
    from_date=date.today(),
    to_date=date.today() + timedelta(days=7),
)

Reschedule

Cancel and reschedule an existing notification:

app.notifications.reschedule(
    reminder,
    at=datetime.now() + timedelta(hours=2),
)

Notification Actions

Actions appear as buttons on the notification. Users can tap them to trigger callbacks without opening the app.

from nib import Notification, NotificationAction, TextInputNotificationAction

notification = Notification(
    title="New Message",
    body="Alice: Hey, are you free for lunch?",
    actions=[
        NotificationAction(
            id="reply",
            title="Reply",
            text_input=TextInputNotificationAction(
                button_title="Send",
                placeholder="Type your reply...",
            ),
        ),
        NotificationAction(
            id="mark_read",
            title="Mark as Read",
        ),
    ],
)

app.notifications.push(notification)

Handling action callbacks

Register a handler to receive action events:

def handle_action(notification_id, action_id, user_text):
    if action_id == "reply" and user_text:
        print(f"User replied: {user_text}")
    elif action_id == "mark_read":
        print("Marked as read")

unsubscribe = app.notifications.on_action(handle_action)

# unsubscribe()

The callback receives three arguments:

Action options

from nib import NotificationAction, NotificationActionOption

NotificationAction(
    id="delete",
    title="Delete",
    options=[
        NotificationActionOption.DESTRUCTIVE,            # Red button
        NotificationActionOption.AUTHENTICATION_REQUIRED, # Requires unlock
    ],
)

NotificationAction(
    id="open",
    title="Open App",
    options=[NotificationActionOption.FOREGROUND],  # Brings app to front
)

Canceling Notifications

# Cancel a specific notification by ID
app.notifications.cancel_notification(notification_id)

# Cancel all scheduled and delivered notifications
app.notifications.cancel_all_notifications()

Querying Notifications

Retrieve notifications that are currently scheduled or displayed in Notification Center:

# Get all scheduled (pending) notifications
def on_scheduled(notifications):
    for n in notifications:
        print(f"Scheduled: {n.title} (id: {n.id})")

app.notifications.get_all_scheduled_notifications(on_scheduled)

# Get all delivered (visible in Notification Center) notifications
def on_delivered(notifications):
    print(f"{len(notifications)} notifications showing")

app.notifications.get_all_delivered_notifications(on_delivered)

# Get a specific notification by ID
app.notifications.get_scheduled_notification(
    "some-id",
    callback=lambda n: print(f"Found: {n.title}") if n else print("Not found"),
)