Quick Start

Build your first Shaft application in minutes.

Getting Started

Since Shaft is just a regular Swift package, the easiest way to start is using the official starter template CounterTemplate:

# Clone the template
git clone https://github.com/ShaftUI/CounterTemplate.git

# Navigate to directory
cd CounterTemplate

# Run the app
swift run

That’s it! You should see a window with a counter application:

Counter Demo
Advanced: Creating a Project from Scratch

For experienced developers who want to set up a project manually:

1. Initialize a new Swift project

mkdir MyShaftApp
cd MyShaftApp
swift package init --type executable

2. Initialize Swift Package

Update the Package.swift file to include the Shaft framework:

// swift-tools-version: 5.10
import PackageDescription

let package = Package(
    name: "MyShaftApp",
    platforms: [.macOS(.v13)],
    dependencies: [
        .package(
            url: "https://github.com/ShaftUI/Shaft.git",
            branch: "main"
        )
    ],
    targets: [
        .executableTarget(
            name: "MyShaftApp",
            dependencies: [
                .product(name: "Shaft", package: "Shaft"), // The core Shaft framework
                .product(name: "ShaftSetup", package: "Shaft"), // Desktop backend
            ]
        )
    ]
)

3. Write Your First App

Create Sources/MyShaftApp/main.swift with your app code:

import Shaft
import ShaftSetup

Shaft

runApp(
    Text("Hello, Shaft!")
)

4. Run Your App

swift run

Understanding the Code

Let’s break down the hello world example:

Imports

Import the required frameworks to access widgets and functionality:

import Observation
import Shaft
import ShaftSetup
  • Observation: Swift’s built-in observation framework for reactive programming
  • Shaft: The core UI framework
  • ShaftSetup: Automatically configures the best backend for your platform

Backend Setup

Initialize Shaft with the default backend:

// Use the default backend
ShaftSetup.useDefault()

The Shaft core framework is platform-agnostic by design—it doesn’t depend on any specific OS or windowing system. This makes it easy to port and customize, but it means you need to provide a backend for windowing and rendering.
ShaftSetup simplifies this by automatically configuring the best backend for your platform.

Hot Reload Setup (Optional)

For faster development, enable hot reloading:

#if DEBUG && !os(Windows)
    import SwiftReload
    LocalSwiftReloader(onReload: backend.scheduleReassemble).start()
#endif

This automatically reloads your app when you make changes to the source code.

Observable State

Create reactive state using Swift’s @Observable macro:

@Observable class Counter {
    var count = 0
}

let counter = Counter()
  • @Observable: Makes the class automatically notify UI when properties change
  • var count: The state that will trigger UI updates when modified

Widget Definition

final class CounterView: StatelessWidget {
    func build(context: BuildContext) -> Widget {
        Column(mainAxisSize: .min, spacing: 16) {
            Text("Count: (counter.count)")
            
            Button {
                counter.count += 1
            } child: {
                Text("Increment")
            }
        }
        .center()
    }
}
  • StatelessWidget: A widget that doesn’t maintain internal state
  • Column: Arranges children vertically with spacing
  • Button: Interactive widget with click handler
  • Text: Displays content, automatically updates when counter.count changes

Running the App

runApp(
    CounterView()
)

Launches the application with your root widget.

Building a Counter App

Let’s create something more interactive:

Interactive Counter

Next Steps

Now that you have a working Shaft application: