Swift is a great language for building command-line tools. Package managers, code generators, deployment scripts — they're all natural fits. But when it comes to collecting input from the user, the standard library leaves you with print and readLine, and not much else.
The result is usually something like this:
print("Enter your project name:")
let name = readLine() ?? ""
It works, but there's no placeholder to guide the user, no inline validation feedback, no way to present a list of choices, and no graceful handling of Ctrl+C. For anything beyond the simplest script, you end up writing infrastructure instead of features.
Promptberry fills that gap. It's a Swift library for building interactive CLI prompts — text input, selects, confirmations, spinners, progress bars, and more.
The most common prompt is text. It supports a placeholder shown when the field is empty, and an optional validate closure that returns an error message if the input is invalid.
let name = try Promptberry.text(
"Project name?",
placeholder: "MyApp",
validate: { $0.isEmpty ? "Name cannot be empty." : nil }
)
Validation runs inline — the error appears immediately below the field without clearing what the user typed. For password fields, swap text for password and the input is masked automatically.
let secret = try Promptberry.password("Choose a password:", mask: "•")
Selecting from a List
When the user needs to pick one option from a fixed set, select renders a navigable list. Arrow keys or j/k to move, Enter to confirm.
let projectType = try Promptberry.select(
"Project type:",
options: ["Executable", "Library", "Plugin"]
)
If none of the built-in options apply, pass allowOther: true to let the user type a custom value instead.
For longer lists, autocomplete filters options as the user types, making it much faster to find the right entry.
let license = try Promptberry.autocomplete(
"License:",
options: ["MIT", "Apache 2.0", "GPL-3.0", "BSD-2-Clause", "MPL-2.0"],
placeholder: "Type to filter..."
)
When multiple selections are needed, multiselect lets the user toggle items with Space and confirm with Enter.
let features = try Promptberry.multiselect(
"Include extras:",
options: ["Tests", "CI workflow", "README", "SwiftLint"],
initialValues: ["Tests", "README"]
)
Confirmation
Before performing an irreversible action, always give the user a chance to back out.
let confirmed = try Promptberry.confirm(
"Create \"\(name)\"?",
active: "Yes, create it",
inactive: "No, cancel"
)
guard confirmed else {
Promptberry.cancel("Nothing created.")
exit(0)
}
Multi-line Input
For freeform input that spans multiple lines, multiline keeps the user in an editable buffer. Each Enter adds a new line, and Ctrl+D submits.
let description = try Promptberry.multiline(
"Short description:",
placeholder: "What does this project do?"
)
Async Work with Spinners and Progress
Once the user has confirmed, the real work begins. For a sequence of async steps, tasks runs each one in order with its own animated spinner.
try await tasks([
PromptTask("Scaffolding project structure") { try await scaffold() },
PromptTask("Writing Package.swift") { try await writeManifest() },
PromptTask("Installing dependencies") { try await installDeps() },
])
If a task throws, its spinner switches to an error indicator and the sequence stops. For operations where you know the total number of steps, a progress bar communicates more than a spinner.
let p = Promptberry.progress(total: files.count, message: "Copying files...")
for file in files {
try await copy(file)
await p.advance()
}
await p.complete("All files copied!")
Handling Cancellation
Every prompt throws PromptCancelled when the user presses Ctrl+C. Wrap your prompts in a do/catch to handle it without crashing.
do {
let name = try Promptberry.text("Project name?")
// ...
} catch is PromptCancelled {
Promptberry.cancel("Cancelled.")
exit(0)
}
Framing the Session
intro and outro add a clear start and end to the interaction, making the tool feel deliberate rather than abrupt.
Promptberry.intro("New Swift Project")
// prompts...
Promptberry.outro("Happy coding!")
Top comments (0)