A keyboard engine built to be unremarkable
PressboardKit is a Swift 6 keyboard engine for iOS. Its entire ambition is that nobody typing on it notices it is there.
Why it exists
The engine was written to replace a commercial keyboard SDK inside an app that already shipped a custom keyboard — Rewordo, an AI rewriting keyboard for iOS. The brief was narrow: reproduce what the third-party engine did, own the code, and stop scheduling around someone else's releases.
That could have been an internal component. Instead the project took a rule from the first commit: build it for one app, but hold the API to the standard a product would need, so that turning it into a product is additive work, not a rewrite. If a rule ever forces a rewrite, that is the signal to stop and decide deliberately rather than drift.
Four things follow from that, and they are all visible in the source:
- No host dependencies. The engine imports nothing from the app it was built for, and has no third-party runtime dependencies at all — the package manifest lists none.
- A licence seam from day one.
LicenseGateexists, and is already called at the points a paid edition would gate. Today it always returns true. Commercialising it means filling in an implementation, not adding call sites. - Every behaviour is a parameter. 51 switches on
KeyboardBehavior, each defaulting to what the system keyboard does, so an integrator who configures nothing gets iOS. - The integration guide is written for a stranger. It always has been, on the assumption that the next person to embed the engine will not be able to ask us anything.
One goal, and what it costs
There is a single overriding criterion for every visual and interaction feature in the engine: the keyboard must look and behave exactly like the native iOS keyboard, so that a user cannot tell which one they are typing on. It outranks every other rule in the project, including the project's own taste.
The cost is worth stating plainly, because it is the part that is easy to promise and hard to keep. A behaviour that deviates from native is treated as a defect, not a style choice. That rules out most of the easy wins a keyboard SDK usually advertises: a springier key animation, a tidier key shape, a suggestion bar with an opinion of its own. If the difference is visible, the task is not finished.
It also rules out a comfortable way of working. Numbers are not estimated by eye; they are measured from the system keyboard and stored as named constants with tests behind them. Touch behaviour is not accepted from the demo app, which runs inside our own process and has more than once reported a gesture working that the real, out-of-process extension did not have. It is verified in a real keyboard extension, by tapping the actual rendered frames of keys.
One thing we deliberately do not do is publish the results. The measurements, the thresholds and the constants stay in the repository, and this site describes what the keyboard does rather than how to redraw it. That is not secrecy for its own sake — it is simply where the work is, and an engine is a poor product if reading its marketing site is a substitute for licensing it.
Principles
Seven rules the engine is held to
Taken from the project's own design document. They are the reason the codebase looks the way it does.
- Native is the specification. Where a custom flourish and the native keyboard disagree, native wins. Parity is checked side by side against the system keyboard on the same device, in light and dark, before a task is closed.
- Nothing is hard-wired. Every keyboard behaviour is a parameter on KeyboardBehavior, and every default is what iOS does. A feature shipped without a switch is a defect, because the integrator cannot turn it off.
- The engine knows nothing about its host. No import from the app it was built for, no shared app group, no borrowed accent colour. Layouts, styling, suggestions, feedback and licensing all arrive through KeyboardEngineConfiguration.
- public is a contract. Anything public carries a DocC comment, grows additively, and changes only through semantic versioning. The changelog starts at the first version, not at the first customer.
- Measured, not eyeballed. Where a value can be read off the native keyboard, it is read off the native keyboard; where iOS exposes nothing to read, it is measured on a device with a harness built for the purpose. Nothing important in the engine is a plausible-looking constant somebody chose, and each one has a test that fails if it drifts.
- Tested where it actually runs. New engine logic carries at least 95 per cent line coverage. Controller behaviour runs on the simulator, because a macOS test run compiles the iOS paths away and passes without executing them. Touch and hit-testing is proven inside a real keyboard extension with XCUITest — the demo app runs in-process and has repeatedly agreed with behaviour the extension did not have.
- Documentation lands with the change. A user-visible feature reaches the feature list, the changelog and the integration guide in the same commit as the code, or the task is not finished.
The shape of it, in numbers
Every figure here is read from the source, the test suite or the package manifest.
Where the project stands
The engine's functional phase is complete: rendering, layouts, typing behaviour, callouts, autocomplete, slide-to-type, emoji and feedback all work, and are exercised on the simulator and in a real keyboard extension. What remains before a 1.0 is release engineering rather than invention.
No version has been tagged yet. Everything written so far sits under Unreleased in the changelog, which this site renders straight from the repository rather than retelling.
Commercial terms are not settled either. The licence and the access page describe the intended shape and say so where a decision is still open. If you are weighing the engine up for a product, the most useful thing you can do is tell us what you need it to do.
Read the source, or read the docs
The integration guide is the fastest way to judge the API. The repository is the fastest way to judge everything else.