Реализация 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 недель.
Получите консультацию по вашему проекту — мы оценим сложность и предложим оптимальное решение. Свяжитесь с нами, чтобы обсудить детали интеграции.







