Build interactive terminal apps with declarative Swift views and an event-driven runtime that rests when there is nothing to do.
Created and maintained by Philip Niedertscheider.
Twill is a Swift library for terminal user interfaces on macOS and Linux. Describe what should appear on screen with views, and let Twill handle keyboard input, layout, updates, and terminal output. It brings Swift Concurrency to the terminal without making your views manage a render loop.
Note
Twill is in early development and the API is subject to change.
A terminal app needs more than text printed to stdout. It must turn input bytes into keys, keep track of which control has focus, lay out content in character cells, update only what changed, and restore the terminal when it exits. Twill keeps that machinery in the runtime so your views can focus on describing the interface.
If you have used SwiftUI, the view-building style will feel familiar. Compose screens from Text, VStack, and HStack, and use @State to keep values across updates to a mounted view. The state model also describes @Binding for sharing that state with child views, but bindings are not implemented yet.
Keyboard input and timeline deadlines wake the runtime only when there is work to do, so static screens stay idle. Layout happens in terminal cells, and the host writes changed content instead of blindly redrawing everything. The application also owns terminal setup and orderly cleanup, leaving views to describe the interface rather than emit escape sequences.
With Swift 6.4 or later and Just installed, clone the repository and run the Clock example from the repository root in a terminal:
just example ClockPress Ctrl-C to exit. For setup instructions and Linux execution options, see Development.
The example is a small Twill app:
import Foundation
import Twill
@main
struct Clock {
@MainActor
static func main() async throws {
let application = Twill.Application(rootView: ClockView())
try await application.run()
}
}
struct ClockView: View {
var body: some View {
TimelineView(.periodic(from: .now, by: 1)) { context in
Text(context.date.formatted(.dateTime.hour().minute().second()))
}
}
}ClockView describes the screen. TimelineView requests updates on its schedule, while Application owns the terminal session and runs until it stops. The runnable version lives in Examples/Sources/Clock.
- Documentation index for a map of the project docs.
- Architecture for view composition, scheduling, rendering, and lifecycle ownership.
- State and identity for mounted state, bindings, and keyed content.
- Interaction model for focus, keyboard routing, and text editing.
- Development for setup, examples, tests, and macOS/Linux workflows.
Contributions and feedback are welcome. If you want to explore the code or run the checks locally, start with the development guide.
Licensed under FSL-1.1-MIT. This is source-available software with restrictions on competing commercial uses. Each version becomes available under the MIT license on the second anniversary of its release. See the license for the full terms.
