Getting started
Quick start
A working keyboard extension end to end — the controller, the settings screen that drives it, and an in-app preview you can type into.
This page is the shortest complete path: an extension that types, a settings screen in the containing app that changes it, and an in-app preview for when you do not want to switch keyboards to test something. Everything here is real API — the demo app in the repository is the same code.
Before you start, add the package and the extension target.
1. The extension
PressboardKitApp assembles the keyboard: the controller, the SwiftUI view, text editing,
sizing, and the input-trait adaptation that gives an email field its @ key and a URL field its
.com key. Your extension supplies a configuration and nothing else.
import PressboardKitApp
final class KeyboardViewController: PressboardInputViewController {
override func makeConfiguration() -> PressboardConfiguration {
PressboardConfiguration(
behavior: .pressboardDefault, // or .standard for pure native
locales: [.english, .czech] // the first is primary
)
}
}
That compiles into a keyboard that draws itself, types, capitalises sentences, corrects words, suggests the next one, offers emoji, and handles the spacebar cursor drag. The bundled English and Czech frequency lists ship inside the package, so autocomplete works without you supplying a word list.
The two presets
KeyboardBehavior is one Codable value carrying every toggle. Two presets are provided:
.standard— pure native. Every default reproduces the system keyboard..pressboardDefault— native plus a few opinionated changes:uniformKeyColor, no globe or mic key,insertOnKeyDown, themultiTouchInputLayer, andpredictiveKeyTargeting. This is whatPressboardConfigurationuses when you do not pass abehavior.
Start from either and flip fields:
var behavior = KeyboardBehavior.standard
behavior.autoCorrection = false
behavior.layoutTypeOverride = .dvorak
behavior.theme = .midnight
behavior.hapticFeedback = true // needs Full Access
The complete list is in the behaviour reference.
2. Persisting settings across the two processes
Pass the load and save hooks and the controller reloads them on viewWillAppear and saves on
every change. This assumes the BehaviorStore from Installation.
final class KeyboardViewController: PressboardInputViewController {
override func makeConfiguration() -> PressboardConfiguration {
PressboardConfiguration(
behavior: BehaviorStore.load(),
locales: BehaviorStore.loadLocales(),
emojiDefaults: BehaviorStore.defaults,
loadBehavior: BehaviorStore.load,
saveBehavior: BehaviorStore.save,
loadLocales: BehaviorStore.loadLocales,
saveLocales: BehaviorStore.saveLocales
)
}
}
3. A settings screen in your app
PressboardController.behavior is @Published, so a SwiftUI control can bind straight into it.
Languages go through setLocales(_:), which re-resolves the layout.
import SwiftUI
import PressboardKit
import PressboardKitApp
struct KeyboardSettingsView: View {
@ObservedObject var controller: PressboardController
var body: some View {
Form {
Section("Typing") {
Toggle("Auto-capitalisation", isOn: $controller.behavior.autoCapitalization)
Toggle("Auto-correction", isOn: $controller.behavior.autoCorrection)
Toggle("Slide to type", isOn: $controller.behavior.slideToType)
}
Section("Feedback") {
Toggle("Haptics", isOn: $controller.behavior.hapticFeedback)
Toggle("Key sound", isOn: $controller.behavior.keySound)
}
Section("Appearance") {
Picker("Theme", selection: $controller.behavior.theme) {
ForEach(KeyboardTheme.allCases, id: \.self) { theme in
Text(theme.displayName).tag(theme)
}
}
}
Section("Language") {
Button("Czech + English") { controller.setLocales([.czech, .english]) }
Button("English only") { controller.setLocale(.english) }
}
}
}
}
4. An in-app preview
The same controller drives a keyboard inside your app. Point it at any PressboardTextDocument —
StringTextDocument is the in-memory one — and drop in PressboardRootView:
import SwiftUI
import PressboardKitApp
struct PreviewScreen: View {
@StateObject private var controller = PressboardController(
configuration: .init(behavior: .standard)
)
@StateObject private var document = StringTextDocument()
var body: some View {
VStack(spacing: 0) {
Text(document.text)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.padding()
PressboardRootView(controller: controller)
}
.onAppear { controller.document = document }
}
}
This is genuinely useful for iterating on look and behaviour — but it is an in-process preview, not an extension. Touch behaviour, hit-testing and the translucent backdrop all differ out of process. Confirm anything touch-related in the real extension.
What you get out of the box
| Behaviour | Handled by |
|---|---|
| Shift, caps lock, auto-capitalisation | The engine and the controller |
| Smart punctuation, the double-space period, punctuation spacing | The controller, via the pure helpers |
| Autocorrection and spell checking | LayeredSpellChecker built by the controller |
| Word completion and next-word prediction | DictionarySuggestionProvider and NextWordPredictor |
| Slide to type | SwipeDecoder, inside the controller |
| The emoji panel, search and recents | PressboardKitEmoji |
| Keyboard height, including the taller emoji page | PressboardInputViewController |
| Field adaptation and the return key label | adaptToInputType |
Where to go next
- Routing keyboard actions — if you want to intercept input yourself.
- Theming — the style seam and the built-in themes.
- Layouts and languages — adding an arrangement or a language.
- Behaviour reference — every parameter, its type and its default.