Reference
FAQ
The questions that come up while integrating — Full Access, the translucent backdrop, memory, in-app use, testing, and what the engine does not do for you.
Does it need Full Access?
Only for haptics. hapticFeedback needs RequestsOpenAccess, and so does
cursorDragEngageHaptic; without it the system silently ignores the request. Everything else —
typing, autocorrection, suggestions, emoji, slide-to-type, the key click sound — works without it.
The key sound is worth calling out, because it is usually the reason people ask. The engine plays
the system keyboard click itself through AudioServicesPlaySystemSound, so there is no host setup
and no Full Access involved, and it respects the silent switch. The Simulator does not play those
clicks at all — test key sound on a device.
My keyboard is an opaque grey rectangle. Why?
Something behind the SwiftUI view is opaque. With translucentBackground on, the engine draws its
own background fully clear so the system’s translucent backdrop shows through — but the default
input view is not clear, and it sits in front of that backdrop.
host.view.backgroundColor = .clear
view.backgroundColor = .clear
host.view.clipsToBounds = false
view.clipsToBounds = false
PressboardInputViewController does this for you. If you are hosting the SwiftUI view yourself,
you have to. See Theming.
Note also that a non-native theme deliberately turns the glass off — a solid theme would be
hidden behind it.
Can I use a UIVisualEffectView instead?
No, and it is worth knowing why. A custom keyboard is not given the host app’s rendered content, so a material has nothing to blur. It paints a flat grey veil, which reads as exactly the rectangle you were trying to avoid. Clearing everything and relying on the system backdrop is what the native keyboard does.
The top row’s key-pop is cut off at the top edge.
An extension cannot draw outside its own frame — the system clips it, and un-clipping the
system’s container views does not help. Reserve the overflow as headroom inside a slightly taller
keyboard instead: pass it to PressboardKeyboardView(topInset:) and add the same amount to your
input view’s height. NativeMetrics.popOverflow(for:) gives you the amount for the active preview
style. The controller exposes it as popTopInset.
Does the engine touch my text document?
No. The view reports KeyboardAction values and never writes anything. That is deliberate: it
keeps the engine host-agnostic, and it means you can intercept any input before it lands.
PressboardKitApp adds a controller that does own the editing, through a PressboardTextDocument
you hand it — but the core engine does not.
Can I use it inside my app, without a keyboard extension?
Yes. Point a PressboardController at any PressboardTextDocument — StringTextDocument is the
in-memory one — and render PressboardRootView. See
the quick start.
That is also the fastest way to iterate on look and behaviour. It is not a substitute for testing the real extension: an in-app preview runs in your process, the real extension is hosted out of process, and touch handling, hit-testing and the backdrop all differ there.
Which modules do I have to link?
PressboardKitApp alone, if you are subclassing PressboardInputViewController — it depends on
the other four. Otherwise PressboardKit plus PressboardKitLayouts, with
PressboardKitAutocomplete and PressboardKitEmoji added only if you use them. They are separate
products because an extension’s memory budget is a fraction of an app’s.
Does slide-to-type work without the autocomplete module?
The decoder is in the core module, but it decodes against a candidate word list, and that list
comes from a provider. Without PressboardKitAutocomplete or a provider of your own, there is
nothing to decode against.
How do settings from my app reach the keyboard?
Through an App Group. KeyboardBehavior is Codable; persist it in a suite both targets can see
and pass loadBehavior / saveBehavior to PressboardConfiguration. The controller reloads on
viewWillAppear and saves on every change. Full setup in
Installation.
Will an SDK update reset my users’ settings?
No. KeyboardBehavior decodes forward-compatibly — every key is optional and falls back to its
default — so a release that adds a property leaves stored settings intact. With synthesised
Codable it would not: one added property would make every previously stored value fail to decode
at once.
Why does the number pad layout never appear?
iOS does not present a third-party keyboard for .numberPad, .phonePad or .decimalPad fields;
it shows its own pad. Those layouts only apply when you embed the engine as an in-app keyboard.
Over such a field the suggestion bar is also suppressed, because native shows none.
The caret disappears while dragging on the space bar.
Leave cursorDragUpdateInterval at its default. Every caret move is a call across a process
boundary, and a high-refresh display produces them faster than the app you are typing into can
repaint — push them all and the text view never gets an idle moment to draw the caret. The default
coalesces them, which leaves those gaps. 0 restores per-step pushing, which is what native does
— but native is not talking across a process boundary.
Can I add a language?
Adding a language is a data-table change inside PressboardKitLayouts — the letter rows, the
callouts, the locale case — not an engine change, and one we would rather make for you than have
you fork over. KeyboardLocale already carries 73 cases.
Supplying a different word list for an existing language is injection, and goes through
PressboardConfiguration(words:). See
Layouts and languages.
Is the keyboard accessible?
Every key is published as one accessibility element carrying a name — the character in its current
case, or space, delete, shift, return, letters, next keyboard, dictate for the
control keys — the .isKeyboardKey trait, so iOS applies VoiceOver’s typing modes, and an
activation action that types the key directly. Assistive activation delivers no touches, so
without that action neither input pipeline would see anything at all.
Nothing is required of a host for this. If you pass a keyOverlay, it is hidden from assistive
technology automatically so it cannot turn the key into a container and swallow its name.
How do I test touch behaviour?
Not in the in-app preview. An in-app preview runs in your process and the real keyboard does not, and behaviour that works in the first has repeatedly failed in the second — which is a thing worth knowing before you spend a day on it.
Every key carries a stable accessibility identifier — key.character.s, key.shift, key.space
and so on — so an XCUITest can look up a key’s real rendered rect and tap absolute points inside
it. That is the only way to prove a hit-test in the real, out-of-process extension. One trap worth
knowing: a build system will happily run a stale extension binary, so uninstall the app before an
A/B measurement rather than trusting that a rebuild took effect.
Do I need a licence, and what does it cost?
Yes, and nothing. PressboardKit is free — there is no charge for a licence and no tier above it — but it is licensed rather than open source. Register an application by product name and bundle identifier and a licence is issued against it. Full detail on Licensing.
Is there a licence check in the engine?
Not today. The core module carries a LicenseGate seam, but its default, .unrestricted,
unlocks every feature. It takes a closure rather than a licence
key: there is no activation API to call, and no code change to make. A later build will validate
an issued licence at setup, additively. See Licensing.
Something is missing from these docs.
The source is the contract: KeyboardBehavior.swift for the parameters,
PressboardController.handle(_:) for a complete action router, and the demo app in the repository
for a working extension, a settings screen and the UI test harness.