1. Open your first window

Build a Session counter in six lessons: open a window, handle a button click, update a count, arrange controls, add navigation, and finish and test the app. Start with macOS and AppKit. Other platforms are an optional branch after the app works.

On Windows or Linux, start with Windows setup, Linux GTK setup, or Linux Qt setup. This walkthrough uses macOS commands.

You need basic Rust knowledge, a terminal, and a text editor. No previous Day knowledge is required. A target is an operating system and UI toolkit pair; macos-appkit uses Apple’s AppKit toolkit. A Day Piece describes part of the interface; the backend creates its native controls. A window hosts that interface.

1. Prepare your Mac

Use an Apple silicon Mac with full Xcode 27 or newer, Git, and a current stable Rust toolchain installed through rustup. Open Xcode once to finish its installation. See system requirements for SDK setup details.

If xcodebuild selects Command Line Tools instead of full Xcode, select your Xcode installation for this terminal session:

export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer

Adjust that path if you installed Xcode elsewhere. This setting does not install Xcode.

2. Install Day

cargo install --git https://github.com/daybrite/day day-cli

This installs the day command in Cargo’s executable directory. The first installation compiles the CLI and downloads its dependencies, so it may take a few minutes. You do not need mobile SDKs or a developer signing account for this local debug app.

If your terminal says day: command not found, open a new terminal first. For a default Rust installation, you can also make it available in the current terminal:

export PATH="$HOME/.cargo/bin:$PATH"
day --version

Or run "$HOME/.cargo/bin/day" --version directly without changing PATH. See PATH troubleshooting for a permanent fix or a custom installation directory.

Check the installation and AppKit tools:

day --version
day doctor --toolkit appkit

3. Create the project

Choose a directory where you keep your projects. Use an unused name if first-app already exists.

mkdir -p first-app/src first-app/resource/locales/en
cd first-app

Create each file below. All file paths in this and subsequent lessons are relative to this app directory. This small project is written out to teach each part; the CLI’s general-purpose scaffold has a larger starter app.

Create Cargo.toml:

[package]
name = "first-app"
version = "0.1.0"
edition = "2024"

[workspace]

[lib]
name = "dayapp"

[features]
default = ["mock"]
mock = ["day/mock"]
appkit = ["day/appkit"]

[dependencies]
day = { git = "https://github.com/daybrite/day" }

[build-dependencies]
day-build = { git = "https://github.com/daybrite/day" }

Cargo fetches the Day crates from Git and records the selected revision in Cargo.lock. Keep that lockfile with your app. The empty [workspace] makes this project independent of any enclosing Cargo workspace. The default mock feature supports editor checks; day build -t macos-appkit selects the real AppKit backend instead. [lib] name = "dayapp" gives the shared library the name used by main.rs and generated native hosts.

Create Day.toml:

schema = 1

[app]
id = "dev.daybrite.tutorial.session-counter"
title = "Session counter"
build = 1
targets = ["macos-appkit"]

[window]
width = 640
height = 480
min_width = 400
min_height = 320

The app ID identifies this app. Use your own unique ID for a product. The dimensions are initial and minimum window sizes in points, chosen for this desktop exercise. The manifest title is packaging metadata; the window’s visible title comes from the catalog below.

Create build.rs:

fn main() {
    day_build::prebuild_project().expect("day-build: prebuild");
}

Create resource/locales/en/app.ftl:

app_title = Session counter
welcome = Count the sessions you finish today.

Fluent catalogs hold user-facing text. The build script generates typed accessors from these message keys, so you can translate the interface without changing the Rust code.

Create src/lib.rs:

use day::prelude::*;

day::resources!();
day::day_start!(options: res::window_options(), root);

pub fn root() -> impl Piece {
    label(res::str::welcome())
        .padding(Space::L)
        .id(id!(welcome_label))
}

Create src/main.rs:

fn main() {
    day::launch(dayapp::res::window_options(), dayapp::root);
}

day::resources!() exposes the generated res module. res::window_options() installs the catalog and supplies the localized window title and manifest geometry. root() describes the window’s content. Space::L is a framework spacing token. The day_start! macro provides the entry point used by generated native hosts; main.rs supplies a Rust desktop entry point. The symbolic welcome_label ID lets UI tests find this label even after its text is translated.

4. Create the native host

Create the native host source files from the CLI’s built-in template:

day project add-target macos-appkit

The CLI reports that the target is already declared and materializes missing files under platform/macos/, including DayApp.xcconfig and the Swift runner. Keep these generated starter sources. The build derives the Xcode project; you do not need to create it manually.

5. Build and open the window

From first-app, run:

day build -t macos-appkit
day launch -t macos-appkit

The build must exit successfully. The launch opens a window titled Session counter with the text Count the sessions you finish today. A debug build can append build information to the title. Quit the app with Command-Q before continuing. day launch builds again, so later lessons need only that command after an edit. A note about a missing icon master is expected: this app has no custom launcher icon.

Troubleshooting

SymptomCheck and recovery
day: command not foundCheck that Cargo’s executable directory (~/.cargo/bin by default) is on PATH, then open a new terminal.
Doctor rejects XcodeSelect full Xcode 27 or newer with DEVELOPER_DIR, then run day doctor --toolkit appkit. Command Line Tools alone cannot build the host project.
Cargo cannot find dayCheck the Git URLs in Cargo.toml and that your connection can reach GitHub.
A generated res::str name is missingCheck the catalog file path and exact key spelling, then rebuild. Do not write generated Rust files by hand.
The launch cannot find a projectRun it from the directory containing this app’s Day.toml.

Editor support

The VS Code extension provides Day project and target commands in your editor. For AI-assisted work, start with the agent guide. Explore the Gallery for examples of larger apps.

Next step

Continue to 2. Handle a button click.