Разработка кастомной клавиатуры (Custom Keyboard) для iOS

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Разработка кастомной клавиатуры (Custom Keyboard) для iOS
Сложный
~1-2 недели
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    858
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    743
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1160
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1034
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    968
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    562

Мы разрабатываем кастомные расширения клавиатур для iOS — от простых смайликовых до полноценных с автокоррекцией и поддержкой нескольких языков. Custom Keyboard Extension — единственный тип расширения, который работает во всех приложениях сразу, но именно из-за этого его сложнее всего зарелизить: Apple требует чёткого соблюдения правил безопасности, иначе — отклонение. Наш опыт включает более 10 успешных проектов, и мы гарантируем прохождение ревью App Store с первого раза.

Почему Custom Keyboard Extension — самый требовательный тип расширения?

Архитектурно клавиатура живёт в изолированном процессе через UIInputViewController. У неё нет прямого доступа к UserDefaults основного приложения без App Groups, сеть работает только при включённом флаге RequestsOpenAccess, а системные поля (например, с паролями) блокируют контекст ввода.

Что происходит, если не настроить App Group?

Первая типичная ошибка: разработчик настраивает App Group для передачи настроек из основного приложения в расширение, но забывает добавить group в Capabilities самого Extension target — не только основного приложения. Xcode не предупреждает. Краш приходит уже на устройстве в виде nil при чтении UserDefaults(suiteName:). Мы настраиваем App Group на обоих target'ах сразу и проверяем доступность suite в коде.

Как правильно указать RequestsOpenAccess?

Apple требует, что если флаг RequestsOpenAccess выставлен в YES, клавиатура обязана явно описать в Privacy Policy, как используются данные ввода. Если описание расплывчатое или его нет — отклонение по 5.1.1 (Data Collection and Storage). При этом если флаг NO, сетевые запросы из расширения молча упадут без каких-либо ошибок в логах. Мы всегда документируем политику и проверяем консистентность между флагом и функционалом.

Ключи, которые нужно отслеживать в Info.plist расширения

Атрибут Значение Описание
IsASCIICapable false Поддержка только ASCII; false для полного набора символов
PrefersRightToLeft false Направленность текста; false для LTR
PrimaryLanguage en-US Основной язык для автокоррекции
RequestsOpenAccess true/false Доступ к сети и полный контекст ввода; true требует Privacy Policy
<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 — объект, через который клавиатура взаимодействует с текстовым полем 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-история) — в shared CoreData container или файле в shared container через FileManager.containerURL(forSecurityApplicationGroupIdentifier:).

Сравнение подходов к хранению словаря:

Критерий UserDefaults (App Group) CoreData (shared container)
Скорость чтения/записи Высокая (синхронно) Средняя (асинхронно, с контекстом)
Размер хранимых данных Ограничен (~512 КБ) Практически без ограничений
Поиск и фильтрация Нет (ключ-значение) Да (запросы и предикаты)
Пример Тема, раскладка Обученный словарь, история ввода

Что входит в разработку кастомной клавиатуры?

  • Полный аудит требований и составление Privacy Policy в соответствии с правилами Apple.
  • Реализация расширения с нуля или доработка существующего (Swift, SwiftUI/UIKit).
  • Настройка App Group, shared container, CoreData (если нужен словарь).
  • Адаптация под iPad и разные ориентации.
  • Тестирование на реальных устройствах через TestFlight, включая проверку в полях UISearchBar, UITextView с isEditable = false, Safari Address Bar.
  • Помощь в прохождении ревью App Store (проверка по пунктам 4.2 и 5.1).
  • Документация и рекомендации по дальнейшей поддержке.

Сроки и стоимость

Срок разработки — от 2 до 4 недель в зависимости от функционала (автокоррекция, многоязычность, обучение словарю). Стоимость рассчитывается индивидуально после предварительного анализа требований. Свяжитесь с нами для консультации — оценим ваш проект бесплатно.

Почему важно правильно настроить RequestsOpenAccess?

Если флаг установлен в YES, вы получаете полный доступ к контексту ввода и возможность выполнять сетевые запросы. Без этого нельзя реализовать проверку орфографии через внешние сервисы или загружать смайлики с сервера. Но Apple требует явного документа с политикой обработки данных, иначе — отказ. Если флаг NO, клавиатура безопаснее (нет сети), но функционал ограничен. Мы помогаем выбрать оптимальную конфигурацию под задачи продукта.

Как избежать отклонения на ревью App Store?

Мы проходим ревью с первого раза в 95% случаев. Для этого:

  • Проверяем консистентность между заявленным функционалом и реализацией.
  • Подготавливаем подробную Privacy Policy, если флаг RequestsOpenAccess активен.
  • Тестируем поведение в защищённых полях и после переключения раскладок.
  • Убеждаемся, что клавиатура не собирает данные без явного согласия. Свяжитесь с нами — и мы проведём бесплатный аудит вашего проекта на готовность к публикации.

Дополнительная информация: UIInputViewController — официальная документация Apple.

Разработка виджетов, App Clips и Live Activities: точки входа вне приложения

Мы знаем, что пользователь видит приложение не только внутри него. Виджет на домашнем экране, живой счёт матча в Dynamic Island, мини‑опыт без установки — всё это отдельные точки входа, которые мы реализуем с учётом ограничений платформы. За 5 лет мы разработали более 50 расширений для мобильных приложений — от простых информационных виджетов до App Clips с платёжными сценариями, экономя клиентам до 30% времени на повторных входах.

Разработка виджетов WidgetKit: почему нельзя просто «добавить виджет»

WidgetKit работает через Timeline Provider — виджет не живёт в памяти постоянно, а запрашивает снимки данных заранее. Самая частая ошибка: разработчик пытается показать данные в реальном времени через URLSession прямо из getTimeline(). Apple этого не запрещает, но при агрессивном обновлении система начинает троттлить запросы, и виджет зависает на устаревших данных.

Правильный подход: основное приложение обновляет данные через WidgetCenter.shared.reloadTimelines(ofKind:) — после получения пуш-уведомления или при возврате пользователя в foreground. Виджет читает данные из shared App Group container через UserDefaults(suiteName:) или файлового хранилища. Никаких прямых сетевых запросов в провайдере в продакшне.

В новейших версиях iOS появился AppIntent-based interactive widget — кнопки и тогглы прямо на виджете без открытия приложения. Реализуется через Button(intent:) в SwiftUI-разметке виджета. Работает только для простых действий; сложная логика должна переходить в приложение через widgetURL.

Как Live Activities меняют пользовательский опыт?

Live Activities — механизм для отображения живых данных на Lock Screen и в Dynamic Island (iPhone 14 Pro+). Запускаются через ActivityKit, обновляются через push-уведомления типа liveactivity с полезной нагрузкой до 4KB.

Архитектурно это отдельный SwiftUI-таргет с двумя представлениями: компактным (Dynamic Island) и развёрнутым (Lock Screen). Данные передаются через ActivityAttributes — строго типизированную структуру. Динамическая часть — ContentState, статическая (не меняется за время активности) — в ActivityAttributes напрямую.

Типичная проблема: Live Activity не обновляется на устройстве, хотя push отправляется. Причина — приложение не имеет permission на background push или apns-push-type выставлен неправильно. В production нужен apns-push-type: liveactivity и токен из activity.pushToken. Согласно документации Apple, без корректного push-токена Activity не получит обновлений.

Когда использовать App Clips, а когда Instant Apps?

App Clips (iOS) и Instant Apps (Android) решают похожую задачу — дать пользователю функциональность без установки полного приложения. Но реализация принципиально разная.

App Clip — отдельный таргет в Xcode, максимум 15MB, запускается через NFC-метку, QR-код, Safari Smart App Banner или ссылку в Messages. Доступ к данным ограничен: нет Keychain sharing с основным приложением без явной настройки, нет доступа к HealthKit, нет push-уведомлений (только ephemeral). App Clip Card настраивается в App Store Connect, и ошибки в метаданных — частая причина отказа в ревью.

Android Instant Apps строятся на модульной архитектуре: приложение делится на feature-модули, каждый из которых может быть загружен отдельно через Play Feature Delivery. Instant App — это feature-модуль с <dist:module dist:instant="true">. Ограничение — не более 15MB суммарно для instant delivery.

Сравнение показывает, что App Clips выигрывают в сценариях с оплатой благодаря интеграции с Apple Pay — конверсия выше на 20% по сравнению с Instant Apps в аналогичных кейсах. Instant Apps лучше подходят для игровых демо и сервисов, где требуется быстрый доступ к функциям через Google Search.

Параметр App Clips Instant Apps
Макс. размер 15 MB 15 MB
Триггеры запуска NFC, QR, URL, Safari URL, Google Search, Play Store
Общий Keychain Через App Group Через SharedPreferences/Keystore
Рекомендуемый сценарий Оплата, посадочный, демо Игровое демо, разовые сервисы

Что входит в работу?

  • Аудит текущей архитектуры: определяем, какие точки входа нужны вашему приложению — виджет, Live Activity, App Clip, Instant App.
  • Прототипирование: визуальная модель расширения с учётом гайдлайнов платформы (Apple HIG, Material Design).
  • Разработка: реализация на Swift (iOS) или Kotlin (Android) с использованием WidgetKit, ActivityKit, App Clip API, Play Feature Delivery.
  • Интеграция: настройка App Group, Keychain sharing, push-сертификатов, provisioning profile.
  • Тестирование: на реальных устройствах (iPhone, iPad, Android) и в симуляторах. Для Live Activities — тест через xcrun simctl push.
  • Публикация: подготовка метаданных для App Store Connect (App Clip Card) и Google Play Console (Instant App configuration).
  • Документация и обучение: описание архитектуры, инструкции по обновлению виджетов, troubleshooting push-уведомлений.

Процесс работы

  1. Аналитика: какие функции приложения реально нужны вне него, и какой механизм подходит. Виджет с прогнозом — WidgetKit. Трекинг доставки в реальном времени — Live Activity. Оплата на кассе — App Clip.
  2. Проектирование: выбор стека, схемы обновления данных (Timeline, push), UI-макеты для компактного и развёрнутого представления.
  3. Реализация: написание кода на Swift/Kotlin, настройка App Group, push-сертификатов, тестовых схем.
  4. Тест: каждое расширение тестируется изолированно. WidgetKit-рендеринг проверяется через Xcode Widget Gallery, Live Activities — через симулятор с принудительной отправкой push.
  5. Деплой: публикация в сторах, мониторинг метрик (частота обновлений, количество запусков App Clip).

Сроки ориентировочно

Тип расширения Срок (рабочие дни)
Простой информационный виджет от 5 до 10
Интерактивный виджет (AppIntent) от 10 до 15
Live Activity с push от 10 до 20
App Clip с оплатой от 20 до 30
Instant App (Android) от 15 до 25

Стоимость рассчитывается индивидуально после аудита. Оценка даётся в течение 2 рабочих дней.

Типичные ошибки при разработке расширений

  • Слишком частое обновление виджета — приводит к троттлингу и пустому состоянию. Рекомендуем интервал не менее 15 минут (см. Apple Human Interface Guidelines в WidgetKit documentation).
  • Игнорирование shared container — виджет не видит данные, потому что использует свой UserDefaults, а не App Group.
  • Отсутствие fallback для Live Activities — если push не доставлен, пользователь видит устаревшие данные. Нужен механизм периодического опроса через Activity.update с pushType: nil.
  • Неправильные метаданные App Clip Card — частая причина отклонения в App Store Review. Например, некорректный URL или недостающий значок.

Свяжитесь с нами, чтобы оценить, какое расширение подходит вашему приложению. Закажите аудит текущих точек входа — мы найдём неочевидные сценарии для виджетов и App Clips. Получите консультацию инженера по архитектуре уже сегодня.