Skip to main content

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:

  • .go files (your application source)
  • drift.yaml or drift.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 .)
  • vendor
  • platform
  • third_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
FieldDescription
app.nameDisplay name of your app
app.idBundle/package identifier
app.orientationSupported orientations: portrait (default), landscape, or all
app.allow_httpAllow cleartext HTTP traffic (true/false, default false)
app.iconPath to a square PNG (minimum 1024x1024). If omitted, a default icon is used.
app.icon_backgroundHex color for the Android adaptive icon background (#RGB or #RRGGBB, default #FFFFFF).
engine.versionDrift engine version (latest or specific tag)

CLI Reference

CommandDescription
drift init <directory> [module-path]Create a new project
drift devicesList connected devices and simulators
drift run androidRun on Android device/emulator
drift run android --device <name or serial>Run on a specific Android device
drift run android --watchRun with automatic rebuild on changes
drift run iosRun on iOS simulator (default: iPhone 15)
drift run ios --simulator "<name>"Run on specific iOS simulator
drift run ios --device --team-id IDRun on physical iOS device
drift run xtoolRun iOS from Linux via xtool
drift run xtool --device UDIDRun on a specific iOS device via xtool
drift build android|ios|xtoolBuild without running
drift log androidStream Android device logs
drift log android --device <name or serial>Stream logs from a specific Android device
drift log iosStream iOS simulator logs
drift log ios --deviceStream iOS device logs
drift log xtoolStream xtool device logs
drift cleanClear build cache
drift fetch-skiaDownload 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 devices shows 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