macOS (AppKit)
macos-appkit is Day’s most exercised target and the one its own development runs on. AppKit is
driven directly through objc2 — there is no C or Objective-C shim
between your Rust and NSButton.
Getting started
You need Xcode’s command-line tools for the linker and system headers; the App Store Xcode is only
required for day pack signing and notarization.
xcode-select --install
cargo install day-cli
day new app my-app --toolkit macos-appkit
cd my-app
day launch -p macos-appkit
day doctor --toolkit appkit turns the AppKit checks into errors and prints setup help for
anything missing. Packaging produces a .dmg:
day pack -p macos-appkit # unsigned, for local distribution
day pack -p macos-appkit --formats dmg
Signing and notarization happen when the credentials are configured — see packaging.
Caveats and special considerations
day launchruns a bare binary, not a bundle. Anything the OS gates on bundle identity — TCC permission prompts, some system services — only works fromday packoutput. Develop withlaunch, verify permission flows against a packed build.- No Lottie.
day-piece-lottieships UIKit and Android arms only. - Pull-to-refresh is emulated, not native: the piece observes
NSScrollView’s elastic overscroll rather than using a native refresh control (macOS has none). - Screenshots capture offscreen (
cacheDisplayInRect:), which cannot sample video layers orNSVisualEffectViewmaterials — anAVPlayerViewcomes out black inday packcaptures and in the gallery. That is a capture limitation, not a rendering one.
What each piece becomes
Every row was read out of the backend source. Pieces marked external live in their own crates and are only present when you depend on them.
| Day piece | AppKit class |
|---|---|
column / row / stack / section | NSView (a flipped subclass, so Day’s top-left origin applies directly) |
label | NSTextField (labelWithString:) |
button | NSButton |
toggle | NSSwitch |
slider | NSSlider |
text_field | NSTextField (bezeled, editable) |
text_area | NSTextView inside an NSScrollView |
picker().menu() | NSPopUpButton |
picker().segmented() | NSSegmentedControl |
picker().inline() | radio NSButtons in an NSStackView |
progress(f) | NSProgressIndicator (bar) |
spinner() | NSProgressIndicator (spinning) |
divider | NSBox (separator) |
scroll | NSScrollView |
list | NSTableView (headerless, single column) |
nav (split) | NSSplitView; the sidebar sits in an NSVisualEffectView |
nav_menu | NSOutlineView |
tabs | NSTabView |
image | NSImageView |
canvas | a custom NSView replaying the display list in drawRect: |
| dialogs / alerts / prompts | NSAlert (window-modal sheet) |
| file dialogs | NSOpenPanel / NSSavePanel |
| menu bar & context menus | NSMenu |
external activity() | NSProgressIndicator |
external combobox() | NSComboBox |
external search_field() | NSSearchField |
external date_picker() / time_picker() | NSDatePicker |
external webview() | WKWebView |
external media() | AVPlayerView |
external map() | MKMapView |
Day has no checkbox, radio, stepper or table piece: toggle is the only boolean control, radio
behavior is picker().inline(), and list is the only collection. See
pieces for the full vocabulary.
Text measurement goes through AppKit itself — cellSizeForBounds: for labels, TextKit’s layout
manager for the text area, and fittingSize for everything else — so wrapping and hyphenation are
the system’s, not a reimplementation.
Reaching AppKit directly
When you need something Day doesn’t model, a tweak hands you the realized
NSView for a node, so you can set any AppKit property without forking the framework.



