Skip to content
PressboardKit
Quick startBrowse docs

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, the multiTouchInputLayer, and predictiveKeyTargeting. This is what PressboardConfiguration uses when you do not pass a behavior.

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 PressboardTextDocumentStringTextDocument 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

Edit this page on GitHub