Published March 23, 2026 · Updated September 7, 2026
How iOS keyboard extensions work — and why they're powerful
A plain-English explanation of what a custom iOS keyboard extension actually is, how it's built, and what it can do.
By Sebastian Kluger · Founder of KOPY · 8 min read

If you have used Gboard, SwiftKey, or KOPY, you have used an iOS keyboard extension. This is a plain-English engineering explainer: process, RAM, Full Access, why we call an API. It is not the Settings tutorial and not the product thesis. Tutorial: enable a custom keyboard. Thesis: keyboard not an app. Prompt stack: how KOPY generates replies.
Key takeaways
- A keyboard extension is a separate, memory-constrained process that can replace the system keyboard in any text field.
- Apple documents Custom Keyboards, including the Full Access privacy gate for network and pasteboard.
- KOPY stays light by generating on a server. The extension sends context and inserts text.
What a keyboard extension is
It is an app extension that iOS allows to replace the default keyboard in any text input. Not a full app. A component other apps host when the user switches to it. Tap a field in WhatsApp or Mail, iOS shows a keyboard. Which one depends on selection. The extension slots into that position via UIInputViewController.
How they differ from regular apps
Memory-constrained, often discussed around 70MB. No persistent background work. Launched when needed, killed when you switch away. You cannot load a giant local model. You cannot keep a chat socket forever. Fast, small, reliable. We send a small request and get a reply string. That is the architecture of survival.
What Full Access actually means
iOS requires Full Access before a keyboard can make network requests or access the clipboard. Apple wants a conscious opt-in before a keyboard "phones home." It does not grant passwords, other apps' data, or Face ID. It means outbound HTTP and pasteboard read. If testers skip it, KOPY cannot see the copied message. That is not a bug in generate. That is the sandbox.
KPI table for extension health
| Signal | Healthy | Sick |
|---|---|---|
| Launch time | Feels instant | Spinner, then crash |
| Memory | UI + tiny client | Bundling a model |
| Network | Ephemeral URLSession | Background session (do not) |
| Auth | App Group token from main app | Keyboard login form from scratch |
| Insert | textDocumentProxy | UIPasteboard only |
Why this beats an app for replies
App tools require leaving the conversation. The extension is already in the field. For a task you repeat dozens of times a day, removing 5-7 steps is the product. Architecture catalog of the three approaches: chatbot vs companion vs keyboard.
Field notes from ContextKeyKeyboard
We use an ephemeral URLSession because Apple is not joking about background sessions in extensions. App Group UserDefaults for tokens. If the suite name is wrong, you will debug "unauthorized" while the main app looks logged in. I have been that person.
SwiftUI in the keyboard is possible and easy to overbuild. Every animation is RAM. Cut it.
Simulator will lie. Physical device for clipboard and Full Access. I put this in CLAUDE.md because I forgot it once and shipped confidence.
Grammarly's 19-hour writing statistic is why a 200ms extra launch matters. Multiply by volume.
Worked failure modes
Jetsam: we used too much RAM. Symptom: keyboard vanishes. Fix: fewer views, no image cache.
Proxy insert into the wrong field: host app with weird first responder. Symptom: text goes nowhere. User thinks generate failed.
Token expired: main app must refresh. Keyboard should fail readable, not spin.
Pasteboard empty: Full Access or user copied an image. Teach copy text.
Frequently asked questions
Can a keyboard read everything I type? With Full Access, a malicious keyboard could capture keystrokes. Install keyboards like you install banks. We generate from copied context plus your taps on our UI, not by scraping your bank PIN. Still: do not use third-party keyboards on password fields if you can avoid it.
Why not on-device LLM? Size, RAM, quality, update cycle. Maybe later. Not in a 70MB box today.
Android? Different extension model. This post is iOS.
Extension engineering stories without turning this into a setup tutorial
UIInputViewController is the whole house
Your UI lives there. Host apps give you a text proxy. You are a guest. Guests who allocate 200MB of images get jetsam. Guests who call background URLSession get rejected by reality.
textDocumentProxy insert
We insert generated text through the proxy so it lands in the field. Pasteboard-only insert is a hack that fights the user. If insert fails, first responder is wrong, not the model.
App Group keys
Main app writes auth. Keyboard reads auth. If keys diverge you get 401 in a pretty UI. This is not Full Access. This is stringly typed storage. I have fat-fingered a key and lost a day.
Ephemeral URLSession
Extensions and background sessions are a bad marriage. We use ephemeral. The explainer exists so future me does not 'optimize' us into a kill.
SwiftUI memory
Pretty gradients are RAM. I cut them. The thesis post talks product. This is the physics under the thesis.
Process death mid-generate
User switched keyboards. Request may complete into a void. UI must tolerate that. Spinners that never die are a bug.
Clipboard types
String vs image vs attributed. We want string. If they copied a screenshot, we should say so, not hallucinate from nothing.
Host app quirks
Some apps wrap fields in ways that break height. We cannot fix Tinder's layout from an extension. We can fail readable.
Why we cannot read the thread without copy
There is no sanctioned 'give me the WhatsApp transcript' API. Copy is the user-granted slice. Anything else is malware territory. I will not go there.
Simulator lies
Clipboard, Full Access, performance. Device or it did not happen. Tutorial tells users to enable. This section tells engineers why their simulator confidence is fake.
70MB folklore
Treat it as a budget, not a dare. Profile. Cut. If you want an LLM in-process, you want a different OS feature.
Security reviews
Assume reviewers think keyboards are keyloggers. Minimize what you collect. Explain Full Access in human language. The Apple doc is your citation, not a blogger.
What I wish Apple's custom keyboard doc said in bold
You will be killed. Design for death. Persist nothing you cannot rebuild from App Group plus a new request.
You will be distrusted. Earn Full Access with a sentence a tired human can read. Then collect almost nothing.
You will not get the host's transcript. Copy is the API. Product people who want 'automatic thread read' want malware. Say no.
You will debug other people's first responders. Budget time for 'it works in Notes.' That is not a tutorial. That is the host-app matrix.
You will want a local LLM. Measure RAM before you promise the blog. I promised myself once. Then I shipped a URLSession.
More questions people ask after reading this
Is 70MB a hard number forever?
Treat it as a budget that moves with OS and device. The lesson is smallness, not a magic constant. Profile on device.
Can I use WebSockets?
You can try. You will still die when the user switches. Design for request/response. I did.
Why not read the field without copy?
There is no honest API for 'give me the WhatsApp transcript.' Anything that captures keystrokes for context is a keylogger shape. We copy on purpose.
Can SwiftUI live in the keyboard?
Yes, carefully. Every view is RAM. UIKit is fine too. Do not import a design system the size of a social app.
How do tokens get there?
App Group from the container app. If that sentence is new, read the thesis plus our SharedDefaults. This explainer stays at mechanism, not a Settings walkthrough.
Will Apple kill third-party keyboards?
They could make life harder. The doc still exists. We build on what is documented, not on rumors.
Engineer checklist before you copy our architecture
Ephemeral URLSession. App Group for tokens, not Keychain theater in the extension unless you know why. textDocumentProxy for insert. Copy for context, not keystroke capture. Profile RAM on a device. Assume death. Fail readable on 401. Do not background. Do not ship a 7B model in the bundle. Do not promise thread read. Read Apple's custom keyboard doc until the Full Access paragraph is boring. Then write the Settings tutorial as a separate URL so this page can stay a mechanism explainer.
The product thesis is why we accepted this. The roundup is who else lives here. The generate post is what we send over the wire. This page is the process box. If you mix them, you get a blob that ranks for nothing and helps no engineer. I wrote too many blobs in 2024. I am not doing it again.
When the host app's first responder is cursed, you will think generate failed. Log insert. Log clipboard type. Log token presence. Then you will stop blaming the model for UIKit. That debugging order saved me weeks. It belongs here, not in a how-to-enable article for consumers.
Keyboard extensions also cannot pretend they are a full app when it is time to explain privacy. The system dialog is scary because it should be. Your job is to make the actual data flow boring: copied string, mode, tone, compact prompt, text back, insert. If your architecture needs a second channel, you are building spyware. I will not help you. This explainer is for people who want a reply in the field without becoming a keylogger. That ethical line is part of the mechanism. It is why copy is the API. It is why we do not 'just read the thread.' Read Apple. Then stop asking for the transcript.
Sources
Source — Apple Developer Documentation, Custom Keyboards, retrieved 2026-09-07.
Source — Grammarly / Harris Poll, 2024 State of Business Communication, retrieved 2026-09-07.