Nib integrates with Swift Charts to provide native, GPU-accelerated data visualizations. You define charts using a declarative API: provide your data as a list of dictionaries, and specify mark types that map data fields to visual properties.
Creating a chart¶
import nib
chart = nib.Chart(
data=[
{"month": "Jan", "sales": 100},
{"month": "Feb", "sales": 150},
{"month": "Mar", "sales": 200},
{"month": "Apr", "sales": 175},
{"month": "May", "sales": 250},
],
marks=[nib.LineMark(x="month", y="sales")],
width=350,
height=220,
)
The Chart constructor takes:
| Parameter | Type | Description |
|---|---|---|
data |
list[dict] |
List of data rows as dictionaries |
marks |
list[BaseMark] |
List of mark objects defining the visualization |
x_axis |
ChartAxis |
X-axis configuration (optional) |
y_axis |
ChartAxis |
Y-axis configuration (optional) |
legend |
ChartLegend or bool |
Legend configuration, or False to hide |
chart_background |
str |
Background color for the entire chart |
plot_background |
str |
Background color for the plot area only |
Standard view modifiers (width, height, padding, corner_radius, etc.) are also supported.
Data format¶
Data is a list of dictionaries where each dictionary represents a row. The keys correspond to the field names used in your marks:
data = [
{"month": "Jan", "revenue": 4200, "expenses": 3100},
{"month": "Feb", "revenue": 5100, "expenses": 3400},
{"month": "Mar", "revenue": 4800, "expenses": 3200},
]
Note
Nib converts row-based data to a columnar format internally for efficient serialization. You always work with the row-based format in your Python code.
Mark types¶
LineMark¶
Connects data points with a continuous line. Best for trends over time:
nib.LineMark(x="month", y="sales")
| Parameter | Type | Description |
|---|---|---|
x |
str or PlottableField |
Data field for x-axis |
y |
str or PlottableField |
Data field for y-axis |
foreground_style |
str or PlottableField |
Line color or field for multi-series |
symbol |
SymbolShape or PlottableField |
Marker shape at each point |
interpolation |
InterpolationMethod |
Curve type between points |
line_width |
float |
Line width in points |
opacity |
float |
Opacity from 0.0 to 1.0 |
BarMark¶
Displays data as rectangular bars. Best for comparing categories:
nib.BarMark(x="category", y="value")
| Parameter | Type | Description |
|---|---|---|
x |
str or PlottableField |
Data field for x-axis |
y |
str or PlottableField |
Data field for y-axis |
width |
float |
Fixed bar width in points |
height |
float |
Fixed bar height in points |
foreground_style |
str or PlottableField |
Bar color or field for grouping |
stacking |
StackingMethod |
Stacking mode for multi-series |
corner_radius |
float |
Rounded corner radius |
opacity |
float |
Opacity from 0.0 to 1.0 |
AreaMark¶
Fills the region between a line and a baseline:
nib.AreaMark(x="date", y="revenue", foreground_style="#3B82F6", opacity=0.3)
| Parameter | Type | Description |
|---|---|---|
x |
str or PlottableField |
Data field for x-axis |
y |
str or PlottableField |
Data field for upper boundary |
y_start |
str or PlottableField |
Data field for lower boundary (optional) |
foreground_style |
str or PlottableField |
Fill color or field for multi-series |
interpolation |
InterpolationMethod |
Curve type |
stacking |
StackingMethod |
Stacking mode |
opacity |
float |
Opacity from 0.0 to 1.0 |
PointMark¶
Displays individual data points as symbols. Best for scatter plots:
nib.PointMark(x="height", y="weight", foreground_style="#EF4444")
| Parameter | Type | Description |
|---|---|---|
x |
str or PlottableField |
Data field for x-axis |
y |
str or PlottableField |
Data field for y-axis |
foreground_style |
str or PlottableField |
Point color or field for coloring |
symbol |
SymbolShape or PlottableField |
Marker shape |
symbol_size |
float |
Marker size in square points |
opacity |
float |
Opacity from 0.0 to 1.0 |
SectorMark¶
Creates pie and donut charts:
nib.SectorMark(
angle="value",
foreground_style=nib.PlottableField("category"),
)
| Parameter | Type | Description |
|---|---|---|
angle |
str or PlottableField |
Data field for sector sizes |
foreground_style |
str or PlottableField |
Color or field for per-segment colors |
inner_radius |
float |
Inner radius (0 = pie, >0 = donut) |
outer_radius |
float |
Outer radius |
corner_radius |
float |
Rounded segment corners |
opacity |
float |
Opacity from 0.0 to 1.0 |
RuleMark¶
Draws a reference line (horizontal or vertical):
# Horizontal line at y=100
nib.RuleMark(y=100, foreground_style="#EF4444", line_width=2)
# Vertical line at x position
nib.RuleMark(x="2024-06-15", foreground_style="#6366F1")
| Parameter | Type | Description |
|---|---|---|
x |
value or field | X position for vertical rule |
y |
value or field | Y position for horizontal rule |
x_start / x_end |
value or field | Horizontal bounds for segments |
y_start / y_end |
value or field | Vertical bounds for segments |
foreground_style |
str or PlottableField |
Line color |
line_width |
float |
Line width |
RectMark¶
Draws filled rectangles for heatmaps and range charts:
nib.RectMark(
x="weekday", y="hour",
foreground_style=nib.PlottableField("intensity"),
corner_radius=2,
)
PlottableField¶
Use PlottableField to reference data columns with optional type hints:
from nib import PlottableField
# Simple field reference (equivalent to just passing "month")
nib.LineMark(x=PlottableField("month"), y=PlottableField("sales"))
# With type hint for proper scale selection
nib.LineMark(
x=PlottableField("date", type="temporal"),
y=PlottableField("temperature", type="quantitative"),
)
Available types: "quantitative" (numeric), "nominal" (categorical), "temporal" (dates/times).
Multiple series¶
To display multiple data series with automatic color assignment, pass a PlottableField as foreground_style:
data = [
{"month": "Jan", "value": 100, "series": "Revenue"},
{"month": "Jan", "value": 80, "series": "Expenses"},
{"month": "Feb", "value": 150, "series": "Revenue"},
{"month": "Feb", "value": 90, "series": "Expenses"},
{"month": "Mar", "value": 200, "series": "Revenue"},
{"month": "Mar", "value": 110, "series": "Expenses"},
]
chart = nib.Chart(
data=data,
marks=[
nib.LineMark(
x="month",
y="value",
foreground_style=nib.PlottableField("series"),
),
],
width=350,
height=220,
)
Swift Charts automatically assigns distinct colors to each unique value in the series field.
Combining marks¶
Combine multiple mark types in a single chart for layered visualizations:
chart = nib.Chart(
data=data,
marks=[
nib.AreaMark(x="month", y="sales", foreground_style="#3B82F6", opacity=0.2),
nib.LineMark(x="month", y="sales", foreground_style="#3B82F6"),
nib.PointMark(x="month", y="sales", foreground_style="#3B82F6"),
nib.RuleMark(y=150, foreground_style="#EF4444", line_width=1),
],
width=350,
height=220,
)
This creates an area chart with a line overlay, point markers at each data point, and a horizontal reference line at y=150.
Axis configuration¶
Use ChartAxis to customize axis appearance:
chart = nib.Chart(
data=data,
marks=[nib.BarMark(x="month", y="revenue")],
x_axis=nib.ChartAxis(
label="Month",
position="bottom",
),
y_axis=nib.ChartAxis(
label="Revenue ($)",
grid_lines=True,
format="currency",
grid_color="#333333",
label_color="#666666",
),
)
| Parameter | Type | Default | Description |
|---|---|---|---|
label |
str |
None |
Axis label text |
position |
str |
auto | "bottom", "top", "leading", or "trailing" |
grid_lines |
bool |
False |
Show grid lines |
hidden |
bool |
False |
Hide the axis entirely |
format |
str |
auto | "number", "currency", or "percent" |
values |
list |
None |
Explicit tick mark values |
label_color |
str |
None |
Color for axis labels |
grid_color |
str |
None |
Color for grid lines |
Legend configuration¶
Control the chart legend with ChartLegend:
chart = nib.Chart(
data=data,
marks=[nib.LineMark(x="month", y="value", foreground_style=nib.PlottableField("series"))],
legend=nib.ChartLegend(
position="bottom",
title="Category",
),
)
# Or hide the legend entirely
chart = nib.Chart(
data=data,
marks=[...],
legend=False,
)
| Parameter | Type | Description |
|---|---|---|
position |
str |
"top", "bottom", "leading", "trailing", or "automatic" |
hidden |
bool |
Hide the legend |
title |
str |
Title text displayed above legend items |
InterpolationMethod¶
Control how lines and areas are drawn between data points:
nib.LineMark(
x="month", y="sales",
interpolation=nib.InterpolationMethod.MONOTONE,
)
| Value | Description |
|---|---|
LINEAR |
Straight lines between points (default) |
MONOTONE |
Smooth curve that preserves monotonicity |
CATMULL_ROM |
Smooth cubic spline through all points |
CARDINAL |
Smooth curve with adjustable tension |
STEP_START |
Step at the start of each interval |
STEP_CENTER |
Step at the midpoint between points |
STEP_END |
Step at the end of each interval |
StackingMethod¶
Control how overlapping bars or areas are stacked:
nib.BarMark(
x="month", y="sales",
foreground_style=nib.PlottableField("region"),
stacking=nib.StackingMethod.STANDARD,
)
| Value | Description |
|---|---|
STANDARD |
Stack values on top of each other |
NORMALIZED |
Scale stacks to 100% |
CENTER |
Center stacks (stream graph effect) |
SymbolShape¶
Marker shapes for PointMark and LineMark:
nib.PointMark(x="x", y="y", symbol=nib.SymbolShape.DIAMOND)
Available shapes: CIRCLE, SQUARE, TRIANGLE, DIAMOND, CROSS, PLUS, PENTAGON, HEXAGON.
Reactive data updates¶
Chart data is reactive. Updating it triggers an automatic re-render:
# Replace all data
chart.data = new_data
# Append a single row
chart.append_data({"month": "Jun", "sales": 300})
# Update a specific row
chart.update_data(0, {"month": "Jan", "sales": 120})
# Clear all data
chart.clear_data()
Complete example¶
A sales dashboard with a bar chart, trend line, and live data updates:
import nib
import random
def main(app: nib.App):
app.title = "Sales"
app.icon = nib.SFSymbol("chart.bar.fill")
app.width = 400
app.height = 450
data = [
{"month": "Jan", "sales": 120, "target": 100},
{"month": "Feb", "sales": 150, "target": 130},
{"month": "Mar", "sales": 180, "target": 160},
{"month": "Apr", "sales": 160, "target": 170},
{"month": "May", "sales": 210, "target": 190},
{"month": "Jun", "sales": 240, "target": 220},
]
chart = nib.Chart(
data=data,
marks=[
nib.BarMark(
x="month", y="sales",
foreground_style="#3B82F6",
corner_radius=4,
),
nib.LineMark(
x="month", y="target",
foreground_style="#EF4444",
line_width=2,
symbol=nib.SymbolShape.CIRCLE,
interpolation=nib.InterpolationMethod.MONOTONE,
),
],
x_axis=nib.ChartAxis(label="Month"),
y_axis=nib.ChartAxis(
label="Units Sold",
grid_lines=True,
grid_color="#e5e7eb",
),
chart_background="#ffffff",
width=370,
height=250,
padding=8,
corner_radius=12,
)
total_label = nib.Text(
f"Total: {sum(d['sales'] for d in data)} units",
font=nib.Font.HEADLINE,
)
# Donut chart for breakdown
pie_data = [
{"category": "Online", "value": 450},
{"category": "Retail", "value": 320},
{"category": "Wholesale", "value": 290},
]
donut = nib.Chart(
data=pie_data,
marks=[
nib.SectorMark(
angle="value",
foreground_style=nib.PlottableField("category"),
inner_radius=40,
outer_radius=70,
corner_radius=3,
),
],
legend=nib.ChartLegend(position="bottom"),
width=370,
height=160,
)
app.build(
nib.VStack(
controls=[
total_label,
chart,
nib.Text("Sales by Channel", font=nib.Font.SUBHEADLINE),
donut,
],
spacing=12,
padding=16,
)
)
nib.run(main)