Getting Started
This guide walks you through installing Drift and running your first app.
Prerequisites
Go
Install Go 1.24 or later from go.dev.
Android
- Android SDK (via Android Studio or command-line tools)
- Android NDK
- Java 17+
- Target device running Android 10 (API 29) or later
Set these environment variables:
export ANDROID_HOME=/path/to/android/sdk
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/<version>
Ensure an Android device is connected via USB (with USB debugging enabled) or an emulator is running.
iOS
On macOS: Install Xcode from the App Store.
On Linux: See iOS on Linux with xtool to build iOS apps without a Mac.
1. Install the CLI
go install github.com/go-drift/drift/cmd/drift@latest
Ensure $(go env GOPATH)/bin is in your PATH:
export PATH=$PATH:$(go env GOPATH)/bin
Verify Installation
drift --help
You should see the list of available commands.
2. Create a Project
The quickest way to scaffold a project is with drift init:
drift init hello-drift
cd hello-drift
The argument is the directory to create. The project name is derived from its
basename, so drift init ./projects/hello-drift also works and produces a
project named hello-drift. You can optionally pass a Go module path as a
second argument (e.g. drift init hello-drift github.com/username/hello-drift).
This generates a go.mod and a main.go with a counter demo app. You can run
it straight away (skip to step 3), or replace main.go with a
minimal example to follow along with this guide.
Minimal main.go
Create a directory with a go.mod (go mod init hello-drift) and the following main.go:
package main
import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/drift"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)
func main() {
drift.NewApp(App()).Run()
}
func App() core.Widget {
return widgets.Container{
Color: graphics.ColorWhite,
Child: widgets.Centered(
widgets.Text{
Content: "Hello, Drift!",
Style: graphics.TextStyle{Color: graphics.ColorBlack, FontSize: 24},
},
),
}
}
Adding Initialization
If your app needs to run async setup before the UI appears (opening a database, loading config), use OnInit:
import "context"
func main() {
app := drift.NewApp(App())
app.OnInit = func(ctx context.Context) error {
// Runs in a background goroutine before widgets mount.
// The splash screen stays visible until this returns.
return loadConfig()
}
app.OnDispose = func() {
closeResources()
}
app.Run()
}
See App-Level Init and Dispose for details.
3. Run Your App
Choose your target platform:
Android
drift run android
Requires a connected device or running emulator.
iOS Simulator (macOS)
drift run ios
Runs on "iPhone 15" by default. Specify a different simulator:
drift run ios --simulator "iPhone 16"
List available simulators with xcrun simctl list devices.
iOS Device (macOS)
drift run ios --device --team-id YOUR_TEAM_ID
Requires:
- Xcode 15+ with command line tools installed
- A connected device with developer mode enabled (iOS 17+)
- Your Apple Developer Team ID (find it in Xcode or Apple Developer portal)
iOS from Linux (xtool)
drift run xtool
Requires xtool setup. See iOS on Linux with xtool.
First Run
On first run, Drift downloads Skia binaries for your target platform. This happens once and is cached.
4. Watch Mode
Add --watch to your run command to automatically rebuild and relaunch your app when source files change:
drift run android --watch # or ios --watch, xtool --watch
After the initial build, Drift prints "Watching for changes..." and waits. Try editing main.go, for example changing "Hello, Drift!" to "Hello, World!":
Content: "Hello, World!",
Save the file and the app rebuilds automatically. Press Ctrl+C to stop watch mode.
You can also re-run manually without --watch:
drift run android # or your target
Watch Mode Options
All drift run targets support --watch:
# Android
drift run android --watch
# iOS Simulator (macOS)
drift run ios --watch
# iOS Device (macOS)
drift run ios --device --watch --team-id YOUR_TEAM_ID
# iOS from Linux (xtool)
drift run xtool --watch
Log Streaming
In watch mode, device logs are streamed to your terminal by default. Suppress them with --no-logs:
drift run android --watch --no-logs
Log streaming works differently per platform:
- Android: logs are filtered by Drift-specific logcat tags (
DriftJNI,Go,AndroidRuntime, etc.). Tag-based filtering survives app restarts, so logs continue seamlessly across rebuilds. - iOS Simulator: logs are streamed via
xcrun simctl spawn, filtered by process name (Runner). This survives app restarts, so logs continue seamlessly across rebuilds. - iOS Device: logs are streamed from the device syslog, filtered by process name (
Runner). - xtool: logs are streamed from the device syslog, filtered by app name.
You can also stream logs independently of drift run using the drift log command:
drift log android # Stream Android logs
drift log android --device ID # Stream logs from a specific Android device
drift log ios # Stream iOS simulator logs
drift log ios --device # Stream iOS device logs
drift log ios --device <UDID> # Stream logs from a specific iOS device
drift log xtool # Stream xtool device logs
drift log xtool --device <UDID>
This is useful when your app is already running and you want to attach a log stream without rebuilding.
What Triggers a Rebuild
Only changes to these files trigger a rebuild:
.gofiles (your application source)drift.yamlordrift.yml(project configuration)
Other file types (images, assets, etc.) are ignored by the watcher.
Skipped Directories
The watcher skips these directories to avoid unnecessary rebuilds:
- Hidden directories (names starting with
.) vendorplatformthird_party
Android ABI Optimization
In watch mode, Drift detects the connected device's ABI (e.g. arm64-v8a) and compiles only for that architecture. This significantly speeds up incremental rebuilds compared to a full multi-ABI build.
xtool Notes
When using drift run xtool --watch, Drift attempts to kill and relaunch the app automatically after each rebuild. If the relaunch times out or fails (e.g. the device is locked), you will need to open the app manually on your device.
Configuration (Optional)
Create drift.yaml to customize your app:
app:
name: Hello Drift
id: com.example.hellodrift
orientation: portrait
icon: assets/icon.png
icon_background: "#FFFFFF"
engine:
version: latest
| Field | Description |
|---|---|
app.name | Display name of your app |
app.id | Bundle/package identifier |
app.orientation | Supported orientations: portrait (default), landscape, or all |
app.allow_http | Allow cleartext HTTP traffic (true/false, default false) |
app.icon | Path to a square PNG (minimum 1024x1024). If omitted, a default icon is used. |
app.icon_background | Hex color for the Android adaptive icon background (#RGB or #RRGGBB, default #FFFFFF). |
engine.version | Drift engine version (latest or specific tag) |
CLI Reference
| Command | Description |
|---|---|
drift init <directory> [module-path] | Create a new project |
drift devices | List connected devices and simulators |
drift run android | Run on Android device/emulator |
drift run android --device <name or serial> | Run on a specific Android device |
drift run android --watch | Run with automatic rebuild on changes |
drift run ios | Run on iOS simulator (default: iPhone 15) |
drift run ios --simulator "<name>" | Run on specific iOS simulator |
drift run ios --device --team-id ID | Run on physical iOS device |
drift run xtool | Run iOS from Linux via xtool |
drift run xtool --device UDID | Run on a specific iOS device via xtool |
drift build android|ios|xtool | Build without running |
drift log android | Stream Android device logs |
drift log android --device <name or serial> | Stream logs from a specific Android device |
drift log ios | Stream iOS simulator logs |
drift log ios --device | Stream iOS device logs |
drift log xtool | Stream xtool device logs |
drift clean | Clear build cache |
drift fetch-skia | Download Skia binaries manually |
Troubleshooting
"drift: command not found"
Ensure $(go env GOPATH)/bin is in your PATH:
export PATH=$PATH:$(go env GOPATH)/bin
Android: "ANDROID_HOME not set"
Set the Android SDK path:
export ANDROID_HOME=/path/to/android/sdk
Android: "ANDROID_NDK_HOME not set"
Set the NDK path (find your version in $ANDROID_HOME/ndk/):
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/27.0.12077973
Android: "no devices/emulators found"
- Check
adb devicesshows your device - Enable USB debugging on your device
- Or start an emulator via Android Studio
iOS: "no simulator found"
List available simulators:
xcrun simctl list devices
Use the exact name in quotes:
drift run ios --simulator "iPhone 16"
Next Steps
- Widget Architecture - Build UI with widgets
- Widget Catalog - Detailed usage for every Drift widget
- Layout System - Arrange widgets on screen
- State Management - Handle app state