App¶
The main application class for Nib menu bar applications. App manages the entire lifecycle of a Nib application, including window configuration, view rendering, event handling, system integration, and communication with the Swift runtime.
Constructor¶
nib.App(title=None, icon=None, identifier=None)
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
title |
str | None |
None |
Text displayed in the menu bar next to the icon |
icon |
str | SFSymbol | View | None |
None |
Menu bar icon. An SF Symbol name, an SFSymbol instance, or any View |
identifier |
str | None |
None |
Bundle identifier, used as the UserDefaults domain. Defaults to com.nib.<slug of the title>; a built app uses its bundle identifier. |
Properties¶
Window and Appearance¶
| Property | Type | Description |
|---|---|---|
title |
str |
Text displayed in the menu bar. Readable and writable |
icon |
str | SFSymbol | View |
Menu bar icon. An SF Symbol name, an SFSymbol instance, or any View. A view is rendered at the menu bar height of 22 points, width from its fitting size, and updates when its properties change |
identifier |
str |
Bundle identifier and UserDefaults domain. Derived from the title (com.nib.<slug>) unless set; inside a built app it is the bundle identifier. |
width |
float |
Popover window width in points |
height |
float |
Popover window height in points |
float_on_top |
bool |
Keep the popover open on outside clicks. When True it closes only from code or by clicking the status bar icon again. Default False |
show_quit_item |
bool |
When True, appends a styled "Quit" button at the bottom of the app UI |
Menu and Fonts¶
| Property | Type | Description |
|---|---|---|
menu |
list[MenuItem] |
Right-click context menu items for the status bar icon |
fonts |
dict[str, str] |
Custom fonts registered for the app. Maps font names to file paths or URLs. Fonts placed in assets/fonts/ are auto-detected |
Lifecycle Callbacks¶
| Property | Type | Description |
|---|---|---|
on_appear |
Callable[[], None] |
Called every time the popover opens |
on_disappear |
Callable[[], None] |
Called every time the popover closes |
on_quit |
Callable[[], None] |
Called once when the app quits and, under nib run, when a reload replaces the app |
on_sleep |
Callable[[], None] |
Called when the Mac is about to sleep |
on_wake |
Callable[[], None] |
Called when the Mac wakes |
on_screen_lock |
Callable[[], None] |
Called when the screen locks |
on_screen_unlock |
Callable[[], None] |
Called when the screen unlocks |
on_appearance_change |
Callable[[bool], None] |
Called with dark when the system switches between light and dark |
on_displays_change |
Callable[[], None] |
Called when a display is connected, removed or rearranged |
is_running |
bool |
Read-only. False once the app has quit or been replaced by a reload; background threads should check it |
Settings¶
| Property | Type | Description |
|---|---|---|
settings |
SettingsPage |
Settings page configuration. When set, the preferences window is accessible via Cmd+, |
clipboard |
str |
Write-only setter for clipboard content. Use get_clipboard() for reading |
Service Properties (read-only)¶
| Property | Type | Description |
|---|---|---|
notifications |
NotificationManager |
Push, schedule, and manage macOS notifications |
battery |
Battery |
Read battery level and charging state |
connectivity |
Connectivity |
Check network connectivity status |
screen |
Screen |
Get display info and control brightness |
keychain |
Keychain |
Secure storage for passwords and tokens |
camera |
Camera |
List devices, capture photos, access video frames |
launch_at_login |
LaunchAtLogin |
Control whether the app starts on login |
permissions |
Permissions |
Check and request Camera, Microphone, and Notification permissions |
Methods¶
View Management¶
build(view)¶
Set the root view of the application. If the app is already running, triggers an immediate re-render.
app.build(view: View) -> None
| Parameter | Type | Description |
|---|---|---|
view |
View |
The root view to display in the popover |
body()¶
Override this method in a subclass to define the UI. Returns the root view. Only used in the class-based approach.
app.body() -> View
update()¶
Manually trigger a UI re-render. Use this to force an update when the automatic reactivity system might not detect a change, or to batch multiple changes into a single render pass.
app.update() -> None
Application Lifecycle¶
run()¶
Start the application. Connects to the Swift runtime, performs the initial render, and enters the main event loop. Blocks until the app quits.
app.run() -> None
quit()¶
Quit the application and clean up resources.
app.quit() -> None
Notifications¶
notify(title, body, subtitle, sound, identifier)¶
Send a macOS system notification.
app.notify(
title: str,
body: str | None = None,
subtitle: str | None = None,
sound: bool = True,
identifier: str | None = None,
) -> None
| Parameter | Type | Default | Description |
|---|---|---|---|
title |
str |
none | Notification title (required) |
body |
str | None |
None |
Notification body text |
subtitle |
str | None |
None |
Subtitle shown below the title |
sound |
bool |
True |
Play the default notification sound |
identifier |
str | None |
None |
Unique ID for updating or removing the notification later |
Clipboard¶
get_clipboard(callback)¶
Read clipboard content asynchronously. The callback receives the clipboard string.
app.get_clipboard(callback: Callable[[str], None]) -> None
set_clipboard(content)¶
Set clipboard content. Equivalent to app.clipboard = content.
app.set_clipboard(content: str) -> None
Hotkeys¶
on_hotkey(shortcut, callback)¶
Register a global keyboard shortcut.
app.on_hotkey(shortcut: str, callback: Callable[[], None]) -> None
| Parameter | Type | Description |
|---|---|---|
shortcut |
str |
Key combination, e.g. "cmd+shift+n", "cmd+k" |
callback |
Callable |
Function called when the hotkey is pressed |
hotkey(shortcut)¶
Decorator form of on_hotkey.
@app.hotkey("cmd+shift+n")
def show_window():
pass
System Events¶
event(name)¶
Decorator form of the on_<event> lifecycle properties, the way hotkey is for on_hotkey. Assigning the property and using the decorator do the same thing; each event holds one callback.
@app.event("wake")
def refresh():
pass
@app.event("appearance")
def themed(dark: bool):
pass
| Name | Property |
|---|---|
appear, disappear |
on_appear, on_disappear |
sleep, wake |
on_sleep, on_wake |
screen_lock, screen_unlock |
on_screen_lock, on_screen_unlock |
appearance |
on_appearance_change |
displays |
on_displays_change |
An unknown name raises ValueError. Callbacks run on the event thread like actions; an exception is logged and never stops the app. See the System Events guide.
Settings¶
register_settings(settings)¶
Register a Settings object for persistence. Connects the settings to UserDefaults and waits for initial values to load.
app.register_settings(settings: Settings) -> None
open_settings()¶
Open the settings window programmatically.
app.open_settings() -> None
close_settings()¶
Close the settings window programmatically.
app.close_settings() -> None
Class Methods¶
set_assets_dir(path)¶
Set the assets directory for the application. Relative paths are resolved from the main script directory.
App.set_assets_dir(path: str | Path | None) -> None
resolve_asset(relative_path)¶
Resolve a relative asset path to an absolute path. Returns the input unchanged for absolute paths and URLs. Returns an empty string if the asset is not found.
App.resolve_asset(relative_path: str) -> str
Examples¶
Function-based app (recommended)¶
import nib
def main(app: nib.App):
app.title = "Counter"
app.icon = nib.SFSymbol("number.circle")
app.width = 280
app.height = 150
label = nib.Text("0", font=nib.Font.TITLE)
def increment():
label.content = str(int(label.content) + 1)
app.build(
nib.VStack(
controls=[
label,
nib.Button("Increment", action=increment),
],
spacing=12,
padding=20,
)
)
nib.run(main)
Class-based app¶
import nib
class CounterApp(nib.App):
count = nib.State(0)
def body(self):
return nib.VStack(
controls=[
nib.Text(f"Count: {self.count}", font=nib.Font.TITLE),
nib.Button("Increment", action=self.increment),
],
spacing=12,
padding=20,
)
def increment(self):
self.count += 1
CounterApp(title="Counter", icon="number.circle").run()
View as the menu bar icon¶
Any view works as the icon, and it stays reactive. Full demo: examples/features/custom_icon.py.
import random
import nib
def main(app: nib.App):
chart = nib.Chart(
data=[{"x": i, "y": v} for i, v in enumerate([4, 6, 12, 14, 10, 8, 11, 9])],
marks=[nib.LineMark(x="x", y="y", foreground_style="#ffffff", line_width=1.0)],
x_axis=nib.ChartAxis(hidden=True),
y_axis=nib.ChartAxis(hidden=True),
legend=nib.ChartLegend(hidden=True),
width=30,
height=16,
)
app.icon = chart
def randomize():
chart.data = [{"x": i, "y": random.randint(4, 16)} for i in range(8)]
app.build(nib.Button("Randomize", action=randomize, padding=20))
nib.run(main)
Related¶
- run(): Recommended entry point for function-based apps
- SFSymbol: Menu bar icons
- MenuItem & MenuDivider: Context menu items
- Settings: Persistent settings
- SettingsPage: Preferences window
- Architecture: process model
- Reactivity: patch model
- Context Menu
- Hotkeys and Clipboard
- System Events
- Services:
app.battery,app.keychain, ...