Nib exposes native macOS services through lazy-loaded properties on the app object. Each service communicates with the Swift runtime via a blocking request/response pattern.
Battery¶
Access battery level, charging state, health metrics, and thermal information.
import nib
def main(app: nib.App):
app.title = "Battery"
app.icon = nib.SFSymbol("battery.100")
app.width = 300
app.height = 250
status = app.battery.get_status()
level_text = f"{status.level:.0f}%" if status.level else "N/A"
state_text = status.state.value
charging_text = "Yes" if status.is_charging else "No"
app.build(
nib.VStack(
controls=[
nib.Text("Battery Status", font=nib.Font.HEADLINE),
nib.Divider(),
_row("Level", level_text),
_row("State", state_text),
_row("Charging", charging_text),
_row("Plugged In", "Yes" if status.is_plugged_in else "No"),
_row("Low Power", "Yes" if status.is_low_power_mode else "No"),
],
spacing=6,
alignment=nib.HorizontalAlignment.LEADING,
padding=16,
)
)
def _row(label, value):
return nib.HStack(
controls=[
nib.Text(label, foreground_color=nib.Color.SECONDARY),
nib.Spacer(),
nib.Text(value),
],
)
nib.run(main)
BatteryInfo fields¶
| Field | Type | Description |
|---|---|---|
level |
float or None |
Battery percentage (0--100) |
is_charging |
bool |
Currently charging |
state |
BatteryState |
Detailed state (charging, discharging, full, etc.) |
is_low_power_mode |
bool |
Low Power Mode enabled |
has_battery |
bool |
Device has a battery |
time_remaining |
int or None |
Minutes until empty |
time_to_full |
int or None |
Minutes until fully charged |
is_plugged_in |
bool or None |
Connected to power |
thermal_state |
str or None |
Current thermal state |
wattage |
float or None |
Power draw in watts |
Battery health¶
health = app.battery.get_health()
print(f"Cycle count: {health.cycle_count}")
print(f"Health: {health.health_percent:.1f}%")
print(f"Condition: {health.condition}")
print(f"Temperature: {health.temperature_celsius:.1f}C")
Prevent sleep¶
from nib.services.battery import SleepType
# Prevent idle sleep during a long task
assertion = app.battery.prevent_sleep(
reason="Processing data",
sleep_type=SleepType.IDLE,
)
# ... do work ...
# Allow sleep again
app.battery.allow_sleep(assertion)
Connectivity¶
Check network status, connection type, and WiFi information.
status = app.connectivity.get_status()
print(f"Connected: {status.is_connected}")
print(f"Type: {status.type.value}") # "wifi", "ethernet", "cellular", "none"
print(f"WiFi SSID: {status.ssid}")
print(f"Expensive: {status.is_expensive}") # True for cellular
print(f"Constrained: {status.is_constrained}") # Low Data Mode
ConnectivityInfo fields¶
| Field | Type | Description |
|---|---|---|
is_connected |
bool |
Active network connection |
type |
ConnectionType |
WIFI, ETHERNET, CELLULAR, NONE, OTHER |
is_expensive |
bool |
Metered connection (cellular) |
is_constrained |
bool |
Low Data Mode active |
ssid |
str or None |
WiFi network name |
interface_name |
str or None |
Network interface name |
Example: connectivity indicator¶
status = app.connectivity.get_status()
if status.is_connected:
icon = "wifi" if status.type.value == "wifi" else "network"
color = nib.Color.GREEN
else:
icon = "wifi.slash"
color = nib.Color.RED
nib.HStack(
controls=[
nib.SFSymbol(icon, foreground_color=color),
nib.Text("Online" if status.is_connected else "Offline"),
],
spacing=6,
)
Screen¶
Get display information, control brightness, check dark mode, and take screenshots.
Get screen info¶
info = app.screen.get_info()
print(f"Display: {info.name}")
print(f"Resolution: {info.width}x{info.height} @{info.scale}x")
print(f"Brightness: {info.brightness * 100:.0f}%")
print(f"Refresh rate: {info.refresh_rate}Hz")
print(f"Native: {info.native_width}x{info.native_height}")
Set brightness¶
app.screen.set_brightness(0.5) # 50% brightness
Note
Brightness control only works on the built-in display.
Dark mode¶
# Check dark mode
dark_info = app.screen.get_dark_mode()
print(f"Dark mode: {dark_info.is_dark_mode}")
# Toggle dark mode (requires Accessibility permission for System Events)
app.screen.set_dark_mode(True)
app.screen.set_dark_mode(False)
List displays¶
displays = app.screen.list_displays()
for d in displays:
print(f"{d['name']}: {d['width']}x{d['height']} @{d['scale']}x")
Take a screenshot¶
result = app.screen.screenshot()
if result.success:
result.save("/tmp/screenshot.png")
print(f"Saved {result.width}x{result.height} screenshot")
# Capture a specific region
result = app.screen.screenshot(x=0, y=0, width=500, height=500)
Warning
Screenshots require the Screen Recording permission. macOS will prompt the user on first use.
Keychain¶
Store and retrieve sensitive data (passwords, API tokens) securely in the macOS Keychain.
# Store a credential
app.keychain.set("MyApp", "user@example.com", "secret-password")
# Retrieve a credential
password = app.keychain.get("MyApp", "user@example.com")
if password:
print(f"Password: {password}")
# Check if an entry exists
if app.keychain.exists("MyApp", "user@example.com"):
print("Credential found")
# Delete a credential
app.keychain.delete("MyApp", "user@example.com")
Method reference¶
| Method | Parameters | Returns | Description |
|---|---|---|---|
set |
service, account, password |
bool |
Store or update a credential |
get |
service, account |
str or None |
Retrieve a credential |
delete |
service, account |
bool |
Delete a credential |
exists |
service, account |
bool |
Check if a credential exists |
Tip
Use the app name as the service parameter and a username or identifier as the account parameter.
Camera¶
List camera devices, capture photos, and stream video frames.
List devices¶
devices = app.camera.list_devices()
for device in devices:
print(f"{device.name} (id={device.id}, position={device.position.value})")
Capture a photo¶
# Capture with the default camera
frame = app.camera.capture_photo()
frame.save("/tmp/photo.jpg")
print(f"Captured {frame.width}x{frame.height} {frame.format}")
# Capture with a specific device
frame = app.camera.capture_photo(device_id="some-device-id", format="png")
Stream video frames¶
from nib.services.camera import CameraFrame
def on_frame(frame: CameraFrame):
# Process frame (e.g., for ML inference)
print(f"Frame: {frame.width}x{frame.height}")
# Start streaming at 15 FPS
app.camera.start_stream(on_frame, fps=15)
# Later, stop streaming
app.camera.stop_stream()
Warning
Camera access requires the Camera permission. Use app.permissions.request(nib.Permission.CAMERA) to request it.
Launch at Login¶
Enable or disable automatic app launch when the user logs in. Uses SMAppService on macOS 13+.
# Check current state
if app.launch_at_login.is_enabled:
print("App launches at login")
# Enable (always in response to user action)
app.launch_at_login.set(True)
# Disable
app.launch_at_login.set(False)
Toggle with a control¶
nib.Toggle(
"Launch at Login",
is_on=app.launch_at_login.is_enabled,
on_change=lambda is_on: app.launch_at_login.set(is_on),
)
Warning
Per Mac App Store guidelines, Launch at Login should only be enabled in direct response to a user action (e.g., a toggle or button click). Do not enable it silently.
Permissions¶
Check and request Camera, Microphone, and Notification permissions through a unified API.
Check a permission¶
from nib.services.permissions import Permission, PermissionStatus
status = app.permissions.check(Permission.CAMERA)
if status == PermissionStatus.AUTHORIZED:
print("Camera access granted")
elif status == PermissionStatus.NOT_DETERMINED:
print("Permission not yet requested")
elif status == PermissionStatus.DENIED:
print("Camera access denied")
elif status == PermissionStatus.RESTRICTED:
print("Camera access restricted by policy")
Request a permission¶
granted = app.permissions.request(Permission.CAMERA)
if granted:
print("Camera access granted")
else:
print("Camera access denied")
Available permissions¶
| Permission | Constant |
|---|---|
| Camera | Permission.CAMERA |
| Microphone | Permission.MICROPHONE |
| Notifications | Permission.NOTIFICATIONS |
Permission flow example¶
import nib
from nib.services.permissions import Permission, PermissionStatus
def main(app: nib.App):
app.title = "Permissions"
app.icon = nib.SFSymbol("lock.shield")
app.width = 280
app.height = 200
status_label = nib.Text("Checking...", foreground_color=nib.Color.SECONDARY)
def check_camera():
status = app.permissions.check(Permission.CAMERA)
if status == PermissionStatus.AUTHORIZED:
status_label.content = "Camera: Authorized"
elif status == PermissionStatus.NOT_DETERMINED:
granted = app.permissions.request(Permission.CAMERA)
status_label.content = f"Camera: {'Authorized' if granted else 'Denied'}"
else:
status_label.content = f"Camera: {status.value}"
app.build(
nib.VStack(
controls=[
nib.Text("Permission Check", font=nib.Font.HEADLINE),
nib.Button("Check Camera", action=check_camera),
status_label,
],
spacing=12,
padding=24,
)
)
nib.run(main)