UIKit & AppKit

TerminalView resolves to UITerminalView on UIKit and AppTerminalView on AppKit. Catalyst follows UIKit.

View setup

import GhosttyTerminal

let terminalView = TerminalView(frame: .zero)
let controller = TerminalController(configuration: .default)

terminalView.controller = controller
terminalView.configuration = TerminalSurfaceOptions(
    backend: .inMemory(session)
)
terminalView.delegate = coordinator

The view owns the native input layer and the Metal-backed surface. TerminalController owns Ghostty app lifecycle, configuration resolution, themes, and surface creation.

Delegate callbacks

Adopt the delegate protocols that match your host UI. A single coordinator object can implement multiple protocols.

final class Coordinator:
    TerminalSurfaceTitleDelegate,
    TerminalSurfaceGridResizeDelegate,
    TerminalSurfaceBellDelegate,
    TerminalSurfaceLifecycleDelegate
{
    func terminalDidChangeTitle(_ title: String) {}
    func terminalDidResize(_ size: TerminalGridMetrics) {}
    func terminalDidRingBell() {}
    func terminalDidAttachSurface(_ surface: TerminalSurface) {}
    func terminalDidDetachSurface() {}
}

Programmatic input

Both platform views press keys through sendKey(_:) and paste text through paste(text:). A key press takes libghostty's key path and is encoded the way the terminal asked for; a paste takes the text path and is framed as a paste when the program enabled bracketed paste — so Enter, Tab, and Ctrl chords are keys, never text. sendText(_:) is the deprecated name of paste(text:).

terminalView.paste(text: "ls -la")
terminalView.sendKey(.enter)
terminalView.sendKey(.c, modifiers: .ctrl)

iOS input

UITerminalView conforms to UITextInput. Hardware keys enter through pressesBegan, software keyboard text enters through insertText, and marked text flows through the shared IME handler.

On iOS, the input accessory bar provides Esc, Tab, arrows, symbols, Paste, and sticky Ctrl/Alt/Cmd modifiers. Configure colors through inputAccessoryStyle. The bar and its members exist on iOS only — not on Mac Catalyst, which imports UIKit too — so a target that also builds for Catalyst nests the exclusion inside the UIKit guard.

#if canImport(UIKit)
#if !targetEnvironment(macCatalyst)
terminalView.inputAccessoryStyle = .init(
    regularBackground: .secondarySystemBackground,
    regularForeground: .label,
    activeBackground: .label,
    activeForeground: .systemBackground
)
#endif
#endif

Hosts with a custom keyboard bar can hide the bundled accessory and drive sticky modifiers through the public sticky APIs.

#if canImport(UIKit)
#if !targetEnvironment(macCatalyst)
terminalView.inputAccessoryItems = []
#endif
#endif

The default button list is available as TerminalInputAccessoryItem.defaultItems. Hosts can provide a smaller list while keeping the bundled bar styling and key dispatch behavior.

#if canImport(UIKit)
#if !targetEnvironment(macCatalyst)
terminalView.inputAccessoryItems = [
    .esc,
    .ctrl,
    .alt,
    .command,
    .divider,
    .tab,
    .arrowLeft,
    .arrowRight,
    .paste,
]
#endif
#endif