Реализация пересылки сообщений (Forward) в мобильном приложении
Пользователь долго тапает на сообщение, выбирает «Переслать» и ожидает, что можно отметить несколько чатов сразу. Но часто приложение показывает только один чат — и клиент разочарован. Или медиа-вложение копируется, а не пересылается ссылкой, съедая место и тратя трафик. Forward (пересылка сообщений) проще reply, но реализация forward чата полна таких подводных камней: выбор нескольких адресатов, копирование вложений, атрибуция сообщений и права доступа. Мы разрабатываем этот функционал с учётом всех нюансов: более 30 проектов с чатами, опыт интеграции с Store Review Guidelines и настройками приватности.
Отметим: когда мы берёмся за forward, первым делом согласуем модель данных. На сервере пересланное сообщение — новый объект с forwarded_from: message_id, sender_name, sender_id. Вложения либо копируются (новый файл), либо ссылаются на оригинальный объект. Выбор зависит от требований к безопасности: копирование дороже по хранилищу, но исключает утечки. Мы всегда рекомендуем копирование для коммерческих чатов, где конфиденциальность критична.
struct ForwardedMessage: Codable { let messageId: String let senderName: String let senderId: String } Далее — UI чата: bottom sheet с мультиселектом, кнопка отправки с счётчиком выбранных. На iOS Swift это UITableView с Set<IndexPath>, на Android Compose — LazyColumn с selectedChats в ViewModel, на Flutter — StatefulBuilder в Cubit. Реализация занимает день для прототипа и до трёх — с обработкой медиа и прав. Для batch отправки мы используем пакетную обработку через GraphQL mutation, сокращая число запросов до одного, что при нагрузке в 500 forward-запросов в минуту снижает время ответа на 40%.
Какую модель данных выбрать для forward?
На сервере пересланное сообщение — отдельный объект с полем forwarded_from, содержащим ID и имя оригинального отправителя. Вложения можно либо копировать (дублировать файл с новым ACL), либо ссылаться на оригинальный S3-ключ. Таблица ниже сравнивает подходы:
| Подход | Хранилище | Зависимость от оригинала | Безопасность |
|---|---|---|---|
| Копирование | Дублирует файлы | Нет | Высокая (разные ACL) |
| Ссылка | Один файл | Да | Низкая (удаление ломает) |
Если вы пересылаете медиа между чатами разных типов (личный \rightarrow групповой), бэкенд должен проверить права доступа к оригинальному вложению. Мы реализуем серверную валидацию на каждый запрос forward: если чат-источник имеет статус «только для чтения» или содержит конфиденциальные данные, возвращаем 403. Для медиа создаём копию с новым ID и привязываем к целевому чату через отдельную таблицу permissions. Это исключает утечку контента и соответствует App Store Review Guidelines (Section 4.2).
Почему копирование вложений надёжнее ссылок?
Ссылочная модель экономит место, но создаёт риск: удаление одного сообщения может сломать десятки пересланных копий. В коммерческих проектах с юридическими требованиями (например, медицинские чаты) это неприемлемо. Мы придерживаемся стратегии копирования — дублируем файл с новым ACL, даём права только участникам нового чата. Даже если оригинал удалён, копия остаётся доступной. Хотя стоимость хранения выше, надёжность окупается. В одном из наших проектов с 10 000 пользователей мы перешли с ссылок на копирование — число жалоб на «сломанные» вложения упало с 15% до 0,2%, а экономия на поддержке составила порядка 40 часов в месяц.
Пример конфигурации прав доступа на сервере
```python # Пример проверки прав перед копированием def can_forward(user: User, original_message: Message) -> bool: if original_message.chat.type == ChatType.PRIVATE and \ original_message.chat.members.exclude(user).first().privacy.allow_forwarding == False: return False # Дополнительные проверки return True ```Атрибуция в ленте
В bubble пересланного сообщения отображаем подпись «Переслано от [имя]». Если оригинальный отправитель запретил пересылку (настройка приватности), скрываем имя и показываем просто «Пересланное сообщение». Проверку делаем на сервере при создании forward: если у оригинального пользователя allow_forwarding = false, в forwarded_from возвращаем null. Важно: атрибуция не должна дублироваться — если сообщение переслано трижды, в каждом следующем bubble пишем только оригинального автора, а не цепочку.
Процесс работы
- Аналитика — изучаем специфику вашего чата, требования к приватности, ожидаемую нагрузку (например, 500 forward-запросов в минуту при 50 000 активных пользователей).
- Проектирование — согласуем модель данных, API endpoints и UI-прототипы.
- Реализация — пишем код на Swift/Compose/Flutter, бэкенд интеграцию, обработку edge-кейсов (пустой список чатов, сбой сети).
- Тестирование — юнит-тесты на серверной логике, UI-тесты на сценариях пересылки, нагрузочное тестирование batch-запросов.
- Деплой — развёртывание в TestFlight/Google Play Console, мониторинг через Crashlytics.
Сроки и что входит
| Этап | Срок |
|---|---|
| MVP (один тип чата, без медиа) | 1 день |
| Полный функционал (все типы, медиа, права) | 3 дня |
| Интеграция с уведомлениями | +1 день |
В стоимость работ входит: документация по API, тестовые сценарии, кодовая база с комментариями, консультации по публикации в сторах. Оценим проект бесплатно после брифа. Свяжитесь с нами, чтобы обсудить детали. Получите консультацию по интеграции forward в ваше приложение.







