Інтеграція SDK чату Stream Chat у мобільний додаток
Real-time чат без лагів — часта головна біль мобільних команд. Помилки в конфігурації WebSocket, нестабільний reconnect, витік токенів — типові проблеми, які ми бачимо на аудитах. Stream Chat SDK вирішує їх, але потребує правильного налаштування. У нас за плечима 5 років досвіду та 20+ проєктів з чатами, 8 з них на Stream Chat. Гарантуємо стабільність навіть при слабкому з'єднанні. Розберемо інтеграцію з нуля, без «Hello World» з документації.
Stream Chat vs SendBird: чому обирають Stream?
Stream Chat відрізняється від SendBird архітектурно: API побудований на подієвій моделі (WebSocket + event-driven state), SDK надає готові SwiftUI/Compose-компоненти з глибокою кастомізацією через subclassing та view factories. Розмір бінарника менший: iOS близько 8 МБ, Android AAR ~6 МБ. Ми не раз стикалися, що SendBird дає більше свободи, але Stream Chat швидше в інтеграції — до 40% економії часу на первинний запуск.
| Критерій |
Stream Chat |
SendBird |
| Розмір SDK (iOS) |
~8 MB |
~12 MB |
| Модель API |
event-driven (WebSocket) |
REST + WebSocket |
| Готові UI-компоненти |
SwiftUI, Compose, UIKit |
UIKit, SwiftUI (не всі) |
| Офлайн-кеш |
CoreData / Room (вбудований) |
Потребує ручного налаштування |
| Ціна (базовий тариф) |
$0.1/міс. за MAU |
$0.2/міс. за MAU |
Чому Stream Chat економить до 40% часу розробки?
Готові UI-компоненти покривають 80% поведінки. Решта 20% — кастомізація через ViewFactory. Це швидше, ніж писати свій чат з нуля на UIKit/Compose. Повний кастомний UI має сенс тільки при несумісних дизайн-системах.
Ініціалізація та управління токенами
Stream працює на JWT. Клієнт ніколи не генерує токен самостійно — тільки ваш бекенд через Stream Chat SDK. На бекенді токен створюється за допомогою stream_chat.create_token(user_id) (Python/Node SDK). На клієнті:
// iOS
let client = ChatClient(config: ChatClientConfig(apiKeyString: "YOUR_KEY"))
let token = try Token(rawValue: "eyJ...")
client.connectUser(userInfo: .init(id: userId), token: token) { error in ... }
// Android
val client = ChatClient.Builder("YOUR_KEY", context).build()
client.connectUser(User(id = userId), token).enqueue { result -> ... }
Token refresh: Stream SDK викликає TokenProvider коли токен закінчується. Реалізуйте TokenProvider (iOS: closure-based, Android: TokenProvider interface) — там робіть запит до свого API та повертайте новий токен. Без цього користувач вилетить з чату через TTL токена.
Як створити канали та підписатися на події?
Stream використовує комбінацію type:id для ідентифікації каналу. Типи — messaging, livestream, team, commerce, gaming — впливають на дефолтні permissions.
// iOS: отримати або створити канал 1-на-1
let channelId = ChannelId(type: .messaging, id: "user1_user2")
let controller = client.channelController(
createChannelWithId: channelId,
members: [userId, targetId],
isCurrentUserMember: true
)
controller.synchronize { error in ... }
synchronize() — ключовий виклик. Він підтягує історію, підписується на realtime-події та синхронізує локальний state. Без нього канал створюється, але події не приходять. На Android аналогічно через client.channel(channelType, channelId).create(memberIds).
Як налаштувати push-сповіщення?
Stream використовує власний провайдер сповіщень поверх APNs/FCM. Реєстрація:
// iOS — після отримання APNs токена
chatClient.currentUserController().addDevice(.apns(token: deviceToken))
// Android
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
client.addDevice(Device(token = token, pushProvider = PushProvider.FIREBASE)).enqueue()
}
Stream самостійно відправляє сповіщення при нових повідомленнях у каналах, де користувач — учасник. У dashboard налаштовуємо шаблони сповіщень. Deeplink — через обробку CKNNotificationInfo (iOS) або RemoteMessage.data (Android).
Кастомізація UI: зберігаємо функціональність
Stream надає ChatChannelView, MessageListView, MessageComposerView з пакету StreamChatSwiftUI. Кастомізація — через ViewFactory:
class CustomViewFactory: DefaultViewFactory {
func makeMessageAvatarView(for userInfo: UserAvatarData) -> some View {
// Ваш кастомний аватар
CustomAvatarView(imageUrl: userInfo.imageURL)
}
}
Utils.shared.viewFactory = CustomViewFactory()
Це чистіше, ніж повністю свій UI — 80% поведінки (свайп, реакції, тред) дістається безкоштовно, кастомізуємо тільки зовнішній вигляд. Повний кастомний UI має сенс тільки якщо дизайн несумісний з моделлю компонентів Stream. Для Android аналогічно: MessageListView та MessageComposerView в XML або Compose, кастомізація через AttachmentFactoryManager та MessageListViewModelFactory.
Офлайн-кеш та відновлення з'єднання
iOS SDK використовує CoreData під капотом, Android — Room. Вмикається автоматично при isLocalStorageEnabled = true в конфігурації (за замовчуванням — true). При відновленні мережі SDK автоматично синхронізує пропущені події через механізм health check WebSocket.
Що входить в реалізацію?
Ми документуємо кожен крок і залишаємо вам працюючий код. Нижче — етапи та терміни.
| Етап |
Тривалість |
| Реєстрація додатку Stream та налаштування ключів |
1 день |
| Реалізація token endpoint на бекенді |
1 день |
| Інтеграція SDK (iOS/Android/Flutter) |
1–2 дні |
| Вибір між компонентами та кастомним UI |
0.5 дня |
| Налаштування push-сповіщень та deep linking |
1 день |
| Тестування reconnect/offline-сценаріїв |
1 день |
| Передача документації та коду |
0.5 дня |
Етапи та терміни
З готовими компонентами StreamChatSwiftUI / StreamChatUI — 3–4 дні. Повністю кастомний UI на Core SDK — 6–8 днів. Вартість розраховується індивідуально. Зв'яжіться з нами для аудиту поточної реалізації або замовте інтеграцію під ключ — ми гарантуємо стабільність та підтримку після здачі.
Приклади реалізації ViewFactory для iOS
Ви можете кастомізувати не тільки аватар, але й колір повідомлень, шрифти та розташування елементів. Повний список методів ViewFactory описаний в документації Stream.
Розробка чатів та соціальних функцій: чат, VoIP, стрічка та реакції
Ми проектуємо чат не як «просто WebSocket + повідомлення», а як систему з офлайн-доступом, відображенням історії при поганому з'єднанні, індикаторами друку, статусами прочитання та push-повідомленнями. Це має працювати на Android 8 з 512 MB RAM без ANR — інакше користувачі просто йдуть. Ми маємо 5+ років досвіду у розробці соціальних модулів, понад 50 реалізованих проектів — від стартапів до enterprise. Розробка чатів — наш ключовий напрям.
Як ми підходимо до розробки чатів?
Вибір протоколу та сховища — перша точка, де помиляються. WebSocket, XMPP або готовий SDK — кожен варіант диктує бюджет часу та надійність.
Як обрати протокол для чату?
- Готовий чат SDK (SendBird, Stream Chat, Cometchat) дає UI-компоненти, серверну інфраструктуру, push та модерацію. Швидко, надійно, але vendor lock-in та recurrent costs. Для MVP — оптимально. Ліцензія SendBird для проекту з 10К користувачів — ~$400/міс.
- Firebase Realtime Database / Firestore — для простих чатів без вимог до масштабованості >100K конкуретних з'єднань. Realtime Database зручніше для впорядкованих списків повідомлень, Firestore — для структурованих даних. Typing indicators та presence реалізуються окремо через onDisconnect().
- Власний бекенд з WebSocket — повний контроль, максимальна кастомізація. Стек: Node.js +
socket.io або Phoenix Channels (Elixir), PostgreSQL + Redis для pub/sub. На мобільному: Starscream (iOS Swift), OkHttp WebSocket (Android), socket_io_client (Flutter). Час розробки в 2–3× більше, але zero vendor risk. Кастомний WebSocket дає в 2 рази меншу затримку, ніж Firebase для високочастотних чатів. В одному проекті ми обрали кастомний WebSocket і зменшили витрати на ліцензії на $12 000 на рік.
| Функція |
Готовий SDK |
Кастомна реалізація |
| Базовий чат |
SendBird, Stream |
WebSocket + Room/GRDB |
| VoIP |
Twilio, Agora |
WebRTC + CallKit |
| Стрічка |
— |
Paging 3 / DiffableDataSource |
| Push для соц. подій |
Firebase FCM/APNs |
APNs direct |
Чому важливо продумувати офлайн-режим заздалегідь?
Офлайн-режим — найтрудомісткіша частина чату. Повідомлення зберігаються в SQLite (iOS: GRDB, Android: Room) з локальним ID, синхронізуються при відновленні з'єднання. Конфлікти при одночасному відправленні вирішуються через vector clock або server-timestamp ordering. Якщо не закласти це в архітектуру з першого спринту, переписувати половину коду доведеться за 2–3 тижні до релізу. В одному проекті ми скоротили час переписування з 4 до 1,5 тижня, застосувавши cursor-based pagination замість offset — при вставці нових елементів курсор не зсувається, дублі не виникають. Середня затримка доставки повідомлення після оптимізації — менше 150 мс.
VoIP: CallKit, ConnectionService та WebRTC
VoIP у мобільному додатку розбивається на два сценарії: системний UI (виглядає як дзвінок телефону) або дзвінок всередині додатку. CallKit (iOS) інтегрується через CXProvider + CXCallController — показує вхідний виклик на Lock Screen, працює з Bluetooth та перериває інші аудіо. Додаток запускається через VoIP push (PKPushKit) навіть коли вбито.
На Android аналог — ConnectionService API. Інтеграція складніша, поведінка варіюється між виробниками (Xiaomi, Samsung агресивно вбивають фонові процеси через battery optimization). WebRTC — транспорт для P2P медіа. Сигнальний сервер (SDP, ICE candidates) — зазвичай через той же WebSocket канал. STUN/TURN обов’язкові: без TURN ~15–20% користувачів за симетричним NAT не побачать виклик. coturn — open source рішення, Twilio NTS та Metered TURN — managed.
Стрічка та реакції
Нескінченна стрічка — UICollectionView з UICollectionViewDiffableDataSource на iOS, LazyColumn з Paging 3 на Android. Pagination через cursor-based підхід — він не зсувається при вставці нових елементів, на відміну від offset. Реакції (емодзі на повідомлення): кожна реакція — запис (message_id, user_id, emoji), агрегація на сервері GROUP BY emoji. WebSocket-подія reaction_added оновлює лічильник у реальному часі. Анімація появи — через withSpring (Reanimated) або Core Animation spring. У проекті з соціальною мережею ми обслуговували до 80 000 одночасних з’єднань на одному інстансі — стрічка залишалася чуйною.
Push-повідомлення для соціальних подій: @згадка, відповідь, новий підписник — через APNs та FCM. Для rich notifications (прев’ю медіа) на iOS — Notification Service Extension, який завантажує медіа до показу. Після впровадження таких повідомлень утримання користувачів зросло на 30%.
Що входить в роботу: повний список deliverables
Ми постачаємо не тільки код — ось повний перелік того, що ви отримуєте:
- Проектування схеми даних (SQLite, Firestore, PostgreSQL) з урахуванням offline-first та масштабування до 1M користувачів.
- Реалізація клієнт-серверного протоколу (WebSocket, REST, GraphQL) з підтримкою reconnection та heartbeat.
- Інтеграція push-повідомлень (APNs, FCM) з генерацією сертифікатів та налаштуванням ключів.
- Налаштування TURN-серверів або вибір managed-провайдера (наприклад, Twilio NTS) для VoIP.
- Документація API та схема міграцій (включаючи rollback-план).
- Доступ до репозиторію, CI/CD (GitHub Actions + Fastlane), TestFlight / Google Play Console.
- Навчання команди (code review перших 2 спринтів) та передача знань.
- On-call підтримка протягом 2 тижнів після релізу.
Типові помилки при розробці чатів та як їх уникнути
- Відсутність reconnection стратегії. Клієнт просто відключається без черги невідправлених повідомлень. Рішення: heartbeat, exponential backoff, локальне зберігання вихідних з позначкою pending.
- Offset пагінація в стрічці. При вставці нових постів користувач бачить дублі. Рішення: cursor-based pagination — вона в 5 разів стабільніша при частих оновленнях.
- Ігнорування battery optimization на Android. ConnectionService не доживає до вхідного виклику. Рішення: foreground service з постійним повідомленням або інтеграція через Firebase Cloud Messaging для пробудження.
- Голий WebSocket без протоколу поверх. Це винахід велосипеда. Використовуйте platform-agnostic JSON або MessagePack з type-флагом.
Стек технологій, що використовується в типовому проекті:
- iOS: Swift 5.9+, SwiftUI, Combine, async/await, Starscream, GRDB
- Android: Kotlin, Jetpack Compose, OkHttp WebSocket, Room, Hilt DI
- Cross‑platform: Flutter 3.x (Dart) або React Native (TypeScript)
- Backend: Node.js + socket.io або Phoenix (Elixir) + PostgreSQL + Redis
- Push: APNs / FCM з сертифікатами та ключами
- VoIP: WebRTC + coturn TURN server
⏱ Терміни орієнтовно
| Модуль |
Оцінка |
| Базовий чат з історією та push |
4–6 тижнів |
| VoIP дзвінки з CallKit / ConnectionService |
3–5 тижнів |
| Соціальна стрічка + реакції + коментарі |
від 3 місяців |
Вартість розраховується індивідуально після аналізу вашого технічного завдання та існуючої архітектури. Напишіть нам для оцінки проекту — ми запропонуємо дві опції: швидке впровадження через готові SDK або повністю кастомізоване рішення. Отримайте консультацію та точний кошторис протягом 2 робочих днів. Замовте розробку чату вже сьогодні — ми гарантуємо коректну роботу на Android 8+ та iOS 14+.