Реализация предиктивного ввода текста в мобильном приложении
Предиктивный ввод — это не только автодополнение слов. В контексте мобильного приложения это может быть автозаполнение форм на основе истории, предсказание следующего действия пользователя, умные подсказки в поиске или smart-compose в чате. Реализация зависит от того, что именно нужно предсказывать.
Встроенные API платформ
Самый быстрый путь — использовать то, что уже есть в OS.
На iOS UITextInputTraits и UITextField.autocorrectionType дают базовую коррекцию. Для предсказания слов в кастомной клавиатуре — UILexicon и UITextDocumentProxy.documentContextBeforeInput. Apple не открывает свою предиктивную модель для сторонних разработчиков напрямую, но KeyboardExtension получает доступ к контексту документа.
NSSpellChecker на iOS 16+ умеет работать с checkedString(with:range:types:options:inSpellDocumentWithTag:orthography:wordCount:) — возвращает предложения по замене с учётом контекста.
На Android TextServicesManager и SpellCheckerSession дают аналогичный доступ. InputMethodService — для кастомных IME. SuggestionSpan показывает подсказки прямо в тексте.
Кастомный предиктор на TFLite
Когда платформенные API не подходят (специфичная лексика, корпоративный жаргон, нестандартный контекст), нужна своя модель.
Архитектура для next-word prediction: LSTM или небольшой Transformer (GPT-2 small, 117M параметров в fp16 = ~240 МБ). Для мобильного — квантизованная версия до int8, ~60 МБ. На iPhone 14+ инференс < 50 мс на 1 предсказание.
class PredictiveTextEngine {
private var interpreter: Interpreter
private let tokenizer: WordpieceTokenizer
private let vocabSize = 30522
func predict(context: String, topK: Int = 3) -> [WordSuggestion] {
let tokens = tokenizer.encode(context.suffix(128)) // последние 128 токенов
var inputTensor = tokens.map { Int32($0) }
// Подаём на вход — получаем логиты над словарём
try interpreter.copy(&inputTensor, toInputAt: 0)
try interpreter.invoke()
let outputTensor = try interpreter.output(at: 0)
let logits = outputTensor.data.withUnsafeBytes {
Array(UnsafeBufferPointer<Float>(
start: $0.baseAddress!.assumingMemoryBound(to: Float.self),
count: vocabSize
))
}
return topKIndices(logits, k: topK).map { idx in
WordSuggestion(word: tokenizer.decode(idx), score: logits[idx])
}
}
}
Токенизатор — отдельная задача. WordPiece для английского хорошо работает, для русского — SentencePiece с BPE-моделью, обученной на русском корпусе. Размер словаря влияет на скорость: 32k токенов vs 64k.
Автодополнение в поиске: Trie vs ML
Для поиска по фиксированному каталогу (товары, города, пользователи) — Trie-структура на клиенте быстрее и предсказуемее, чем ML. Prefixmatch за O(k) где k — длина запроса. SQLite FTS5 (MATCH "запрос*") — хороший вариант для каталогов до 1M записей с поддержкой fuzzy matching через spellfix1 extension.
ML нужен там, где нужно ранжировать результаты по персональной релевантности, а не только по текстовому совпадению.
Кеширование и латентность
Предиктивный ввод должен отвечать быстрее 100 мс — иначе пользователь уже напечатал следующий символ. Кеш последних N предсказаний в памяти, инвалидируемый при смене контекста. На iOS DispatchQueue с qos: .userInteractive, на Android Dispatchers.Main.immediate в корутине.
Debounce: не вызывайте предиктор на каждый символ, а через 150–200 мс после последнего ввода. Это снижает нагрузку без ухудшения UX.
Процесс работы
Анализ предметной области: какой текст, какой контекст, нужна ли персонализация. Выбор подхода: платформенные API, Trie + FTS, или ML-модель. Подготовка данных для обучения (если кастомная модель). Квантизация и оптимизация модели под мобильный инференс. Интеграция в UI с корректным debounce и кешем. Тестирование на устройствах разного класса.
Ориентиры по срокам
Поисковое автодополнение через Trie/FTS — 2–4 дня. Кастомная ML-модель для next-word prediction с квантизацией и CoreML/TFLite-инференсом — 3–5 недель (включая подготовку данных).







