Розробка кастомної клавіатури (Custom Keyboard) для iOS
Custom Keyboard Extension—один з небагатьох типів iOS-розширень, які отримують доступ до вводу у всіх додатках відразу. Це ж робить його найскладнішим для проходження ревью App Store: Apple ретельно перевіряє відповідність RequestsOpenAccess та реальну поведінку.
Де все ломається ще до релізу
Архітектурно Custom Keyboard працює через UIInputViewController. Це не просто view-контролер—він живе в окремому процесі, ізольованому від host-додатку. Немає доступу до UserDefaults з suite, немає спільного keychain без App Groups, немає сети без флага RequestsOpenAccess: YES в Info.plist.
Перша типична помилка: розробник налаштовує App Group для передачі настроєк від основного додатку в розширення, але забуває додати group до Capabilities самого Extension target—не тільки основного додатку. Xcode не попереджає. Краш приходить на пристрої як nil при читанні UserDefaults(suiteName:).
Друга проблема серйознішаю Apple потребує, що якщо флаг RequestsOpenAccess виставлений у YES, клавіатура обов'язково явно описати в Privacy Policy, як використовуються дані введення. Якщо опис розплисчастий або його немає—відхилення по 5.1.1 (Data Collection and Storage). Якщо флаг NO, мережеві запити з розширення мовчки впадуть без ошибок в логах.
Ключі, які потрібно відстежувати в Info.plist розширення
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>IsASCIICapable</key>
<false/>
<key>PrefersRightToLeft</key>
<false/>
<key>PrimaryLanguage</key>
<string>en-US</string>
<key>RequestsOpenAccess</key>
<true/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.keyboard-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).KeyboardViewController</string>
</dict>
Як влаштована нормальна Custom Keyboard
UIInputViewController надає textDocumentProxy—об'єкт UITextDocumentProxy, через який клавіатура взаємодіє з текстовим полем host-додатку. Вставка тексту: textDocumentProxy.insertText("a"). Видалення символу: textDocumentProxy.deleteBackward(). Переключення мови: advanceToNextInputMode(). Закриття клавіатури: dismissKeyboard().
Важливий момент: textDocumentProxy.documentContextBeforeInput та documentContextAfterInput повертають текст навколо курсора—але не завжди. У деяких полях (protected text fields, поля з атрибутами isSecureTextEntry) proxy повертає nil. Це потрібно обробляти явно, інакше будь-яка логіка автокорекції або передбачення слів сломається на полях з паролями.
Проблема висоти клавіатури
Системна клавіатура адаптується під Safe Area автоматично. Кастомна—ні. Висоту потрібно встановлювати через heightConstraint на inputView та перераховувати при зміні орієнтації пристрою:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let newHeight: CGFloat = view.bounds.width > view.bounds.height ? 180 : 256
if heightConstraint?.constant != newHeight {
heightConstraint?.constant = newHeight
view.layoutIfNeeded()
}
}
Без явного перерахунку на iPhone у landscape-режимі клавіатура або обрізується, або перекриває контент неправильною висотою.
Збереження стану між сесіями
Розширення не має постійного стану в пам'яті—процес завершується з вводом. Настройки (розкладка, тема, автокорекція) зберігайте в UserDefaults через App Group. Дані побільші (навчений словник, emoji-історія)—у спільному CoreData container або файлі в спільному container через FileManager.containerURL(forSecurityApplicationGroupIdentifier:).
Тестування та ревью
Тестувати Custom Keyboard в симуляторі незручно: перемикання клавіатури працює інакше, ніж на реальному пристрої. На симуляторі advanceToNextInputMode() часто не спрацьовує. Використовуйте реальний пристрій з TestFlight з самого початку.
Перед відправкою в ревью: перевірити поведінку в UISearchBar, UITextView з isEditable = false, у Safari Address Bar (розширення там не активується—це нормально, але клавіатура не повинна крашитися).
Ревью App Store окремо перевіряє: немає скритого кейлоггінгу, Privacy Policy відповідає, функціональність відповідає заявленій.
Тривалість розробки: від 1 до 3 тижнів—залежить від наявності власного лексикону, автокорекції, багатомовності. Вартість розраховується індивідуально після аналізу вимог.







