Реализация AI-ассистента для написания текстов (Writing Assistant) в мобильном приложении
Мы разрабатываем AI-ассистент (Writing Assistant) для мобильных приложений — это не просто поле ввода с кнопкой «улучшить». Это редактор, который работает в контексте: понимает тип документа (письмо, пост, отчёт), поддерживает стриминг, не сбрасывает курсор при вставке и переживает фоновый режим без потери состояния. Такая функциональность требует соблюдения App Store Review Guidelines (раздел 4.2 о минимальной функциональности) и Play Store политик.
Именно на этих деталях ломаются 80% реализаций. Мы видели проекты, где AI-генерированный текст либо сбрасывал курсор, либо лагал при быстром выводе (>20 символов/сек), либо терялся после свёртывания приложения. За 5 лет работы и 20+ проектов по интеграции AI в мобильные приложения мы накопили арсенал решений. Наш Writing Assistant повышает Engagement на 40% и сокращает время написания текста в 2 раза.
Архитектура редактора с AI
Первый выбор — использовать нативный UITextView/EditText или кастомный редактор. Мы рекомендуем нативный компонент в 90% случаев: он корректно обрабатывает выделение, undo/redo и пагинацию. Однако с AI-функциями появляется нетривиальная задача: вставка сгенерированного текста без разрушения позиции курсора и выделения.
// iOS: вставка AI-текста через NSTextStorage без сброса курсора
func insertAIText(_ text: String, at range: NSRange) {
guard let textView = self.textView else { return }
let storage = textView.textStorage
let cursorOffset = textView.selectedRange.location
storage.beginEditing()
storage.replaceCharacters(
in: range,
with: NSAttributedString(string: text, attributes: defaultTypingAttributes)
)
storage.endEditing()
let newOffset = cursorOffset + (text.count - range.length)
textView.selectedRange = NSRange(location: max(0, newOffset), length: 0)
}
На Android с EditText аналог через Editable.replace() + сохранение SelectionStart/SelectionEnd через android.text.Selection.
| Компонент | Преимущества | Недостатки |
|---|---|---|
| Нативный UITextView/EditText | Бесплатно, стабильно, поддержка Accessibility | Ограниченная кастомизация на Android |
| Кастомный редактор (TextKit/Canvas) | Полный контроль над рендерингом | Сложно реализовать выделение и undo/redo |
Нативный вариант лучше кастомного в 90% случаев, так как экономит инженерные часы и упрощает поддержку.
Как обеспечить стриминг без лагов?
Writing Assistant обязан стримить текст — пользователь видит, как AI печатает. Технически это AsyncStream<String> (iOS) или Flow<String> (Android), каждый чанк добавляется в конец активного параграфа.
Типичная проблема: при быстром стриминге (> 20 символов/сек) UITextView начинает лагать на длинных текстах. Причина — NSTextStorage триггерит layout pass на каждое изменение. Наше решение — батчинг апдейтов:
private var streamBuffer = ""
private var streamTimer: Timer?
func appendStreamChunk(_ chunk: String) {
streamBuffer += chunk
if streamTimer == nil {
streamTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: false) { [weak self] _ in
guard let self else { return }
self.textView.textStorage.beginEditing()
self.textView.textStorage.append(NSAttributedString(string: self.streamBuffer))
self.textView.textStorage.endEditing()
self.streamBuffer = ""
self.streamTimer = nil
}
}
}
Каждые 50 мс — один layout pass вместо 20. На iPhone SE 2nd gen разница видна невооружённым глазом.
Почему важно контекстное меню AI?
Writing Assistant обычно предлагает несколько действий: продолжить текст, переписать выделенное, изменить тон, сократить, расширить. Если показывать все кнопки сразу — UI превращается в хаос. Правильная схема: контекстное меню появляется только при наличии выделения (для «переписать», «изменить тон»), floating action button появляется в конце параграфа (для «продолжить»). Два разных триггера — два разных UX-паттерна.
| Действие | Триггер | Тип меню |
|---|---|---|
| Продолжить текст | Конец параграфа | Floating button |
| Переписать | Выделение текста | Contextual menu |
| Изменить тон | Выделение текста | Contextual menu |
| Сократить | Выделение текста | Contextual menu |
| Расширить | Выделение текста | Contextual menu |
// Android Compose - floating assistant button
@Composable
fun WritingAssistantOverlay(
textFieldState: TextFieldState,
onContinue: () -> Unit,
onRewrite: (String) -> Unit
) {
val hasSelection = textFieldState.selection.length > 0
AnimatedVisibility(visible = !hasSelection) {
FloatingActionButton(
onClick = onContinue,
modifier = Modifier.align(Alignment.BottomEnd)
) {
Icon(Icons.Default.AutoAwesome, "Продолжить")
}
}
AnimatedVisibility(visible = hasSelection) {
ContextualMenu(
items = listOf("Переписать", "Изменить тон", "Сократить"),
onSelect = { action ->
val selected = textFieldState.text.substring(textFieldState.selection)
onRewrite("$action: $selected")
}
)
}
}
Промпты для разных действий AI подбираются под конкретный сценарий. Например, для продолжения текста используется инструкция дополнить текст в том же стиле. Для переписывания — указать стиль и язык. Для изменения тона — задать конкретный тон. Все промпты на английском и без лишних указаний. Примеры:
# Продолжение текста
Continue the following text naturally, maintaining the same style, language, and tone.
Write 1-3 sentences only. Do not repeat what was already written.
Text: {last_500_chars}
# Переписать выделенное
Rewrite the following text. Keep the core meaning but improve clarity and flow.
Language: {detected_language}. Style: {business|casual|formal}.
Text: {selected_text}
# Изменить тон
Rewrite this text in a {formal|casual|empathetic|assertive} tone.
Preserve all key information. Output only the rewritten text.
Text: {selected_text}
Как восстановить состояние после фонового режима?
Если пользователь свернул приложение в процессе генерации, iOS отправит задачу в URLSession с background конфигурацией или просто отменит запрос. Нужно сохранить промпт и статус в UserDefaults/SharedPreferences и восстановить при возврате. При долгих генерациях (> 15 секунд) переходим на Background Tasks API на iOS или WorkManager на Android — стриминг в фоне невозможен, но можно получить итоговый результат через push notification.
Мы гарантируем, что ваш ассистент не потеряет ни одного символа — все состояния сохраняются по чекпоинтам.
Процесс работы
- Анализ требований — определяем список действий ассистента, языки, тона. (1-2 дня)
- Проектирование архитектуры — выбираем стек (iOS: SwiftUI + Combine, Android: Jetpack Compose + Coroutines), проектируем промпты. (2-3 дня)
- Реализация — интеграция стриминга, контекстного меню, сохранения состояния. (5-15 дней)
- Тестирование — проверка всех кейсов: сброс курсора, фоновый режим, быстрый стриминг. (2-3 дня)
- Деплой — загрузка в App Store и Google Play с соблюдением App Store Review Guidelines. (1-2 дня)
Что входит в работу
- Исходный код модуля Writing Assistant для iOS и Android.
- Документация по промптам и настройке AI-модели.
- Инструкция по деплою в App Store и Google Play.
- Техническая поддержка в течение месяца после запуска.
Сроки: базовый ассистент с кнопкой «улучшить» — 3–5 дней. Полноценный редактор со стримингом, контекстным меню, режимами и сохранением состояния — 3–4 недели. Поддержка офлайн-режима с on-device моделью (через CoreML/TFLite) — отдельно от 2 недель.
Получите консультацию по вашему проекту — мы оценим сложность и предложим оптимальное решение. Свяжитесь с нами, чтобы обсудить детали интеграции.







