Мобильный SDK
Как подключить чат поддержки Revback к мобильному приложению — Android и iOS. Нужны две вещи: наш SDK в приложении и, для уведомлений, Firebase-проект самого приложения.
Что делает SDK
| Версия | 0.1.0 |
|---|---|
| Платформы | Android — Maven Central: revback-chat-ui (готовый экран) и revback-chat-core (ядро);
iOS — Swift-пакет RevbackChat: продукты RevbackChatUI и RevbackChatCore |
| Технология | Нативные библиотеки: Android — Jetpack Compose, iOS — SwiftUI; ничего дополнительно ставить не нужно |
| Адрес API | https://api.revback.ai |
| Что делает | Готовый экран чата в виде виджета вашего сайта, лента и отправка с вложениями, уведомления о новых сообщениях через Firebase, стабильный id пользователя чата |
Готовый экран чата входит в SDK; кто рисует чат сам — подключает только ядро. Firebase SDK подключает само приложение: модуль Firebase за собой не тянет.
Шаг 1. Подключить SDK
Android — зависимость из Maven Central. iOS — пакет через Swift Package Manager.
// build.gradle.kts — репозиторий mavenCentral() в проекте уже есть
implementation("ai.revback:revback-chat-ui:0.1.0") // готовый экран чата, ядро внутри
// только ядро — если экран чата рисуете сами
implementation("ai.revback:revback-chat-core:0.1.0")- Xcode → File → Add Package Dependencies
- Адрес репозитория — в блоке ниже, версия
0.1.0 - Продукт
RevbackChatUI— готовый экран, ядро внутри;RevbackChatCore— только ядро, если экран рисуете сами
https://github.com/revback-ai/revback-chat-iosШаг 2. Инициализация с ключом
Один вызов при старте приложения — ключ канала лежит в конфиге SDK.
// Application.onCreate
RevbackChat.init(this, RevbackChatConfig(widgetKey = "ваш ключ"))// после FirebaseApp.configure(); в Swift указываются все параметры конфига
RevbackChat.shared.configure(config: RevbackChatConfig(
widgetKey: "ваш ключ", apiBaseUrl: RevbackChatConfig.companion.DEFAULT_API_BASE_URL,
liveUnread: true, persistHistory: true, historyPageSize: 50))Где взять ключ: кабинет Revback → Каналы → «Чат в приложении» → поле «Ключ канала».
DEFAULT_API_BASE_URL— этоhttps://api.revback.ai.- В Swift параметры конфига указываются все — значений по умолчанию у них нет.
- Любой вызов до
init/configureбезопасен: no-op с логом. setPushTokenбуферизует токен.- API ничего не бросает.
Шаг 3. Экран чата
Готовый экран повторяет виджет на вашем сайте: шапка в цвете канала, входящие с подписью отправителя, свои сообщения с галочками, вложения, поле ввода. Открывается одним вызовом.
// открыть готовый экран поверх приложения — по тапу на «Поддержка» или по уведомлению
RevbackChatUI.open(context)
// или встроить в свою навигацию (Compose)
RevbackChatScreen(onClose = { navController.popBackStack() })// открыть готовый экран поверх приложения
RevbackChatUI.present(over: rootViewController)
RevbackChatUI.dismiss() // закрыть из кода
// или собрать показ самому
RevbackChatView(onClose: { dismiss() }) // SwiftUI
present(RevbackChatViewController(onClose: { … }), animated: true) // UIKit- Вызов до
init/configure— лог и no-op; повторныйpresentпри показанном экране — тоже. onClose = null— крестика в шапке нет: экран закрывает приложение.- Экран сам сообщает ядру, когда чат на экране: пока он открыт, уведомления о новых сообщениях не показываются.
- Тема у экрана своя, светлая — как у виджета сайта; Material приложения не наследуется.
- Android: Jetpack Compose без Material; просмотр вложений через собственный
FileProvider— настройки не требует. - iOS: минимум iOS 15;
libsqlite3иNSPhotoLibraryUsageDescriptionдобавлять не нужно — пакет линкует библиотеку сам, а выбор фото идёт через системный пикер без доступа к медиатеке.
Свой экран — на ядре: RevbackChat.conversation даёт open() / close(),
ленту messages, send(), вложения через upload() → send(),
loadMore(), markRead(). На Android это потоки, на iOS данные приходят через
RevbackChatDelegate.
Шаг 4. Уведомления
Токен уходит в Revback, входящие уведомления проходят через SDK: он сам отвечает, его это пуш или ваш.
// FirebaseMessagingService
override fun onNewToken(token: String) = RevbackChat.setPushToken(token)
override fun onMessageReceived(message: RemoteMessage) {
when (RevbackChat.onMessageReceived(this, message.data)) { // показ — внутри
is PushAction.NotOurs -> handleOwnPush(message) // пуши самого приложения
else -> Unit
}
}
// при старте (Google рекомендует getToken() на каждом старте)
FirebaseMessaging.getInstance().token.addOnSuccessListener { RevbackChat.setPushToken(it) }
// Activity.onCreate / onNewIntent — тап по уведомлению открывает чат
if (RevbackChat.conversationId(intent) != null) RevbackChatUI.open(this)- Разрешение
POST_NOTIFICATIONS(Android 13+) объявляет и запрашивает приложение. - Канал уведомлений
revback_supportсоздаёт модуль. - Иконка:
RevbackChatNotifications.smallIconOverride = R.drawable.ic_notification. - Строки: английские по умолчанию, русские в
values-ru; другие языки — те же имена ресурсов в своёмvalues-xx.
// MessagingDelegate — provider указывается всегда
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
if let token = fcmToken { RevbackChat.shared.setPushToken(token: token, provider: PushProvider.fcm) }
}
// UNUserNotificationCenterDelegate.willPresent — приложение на экране
let data = stringData(notification.request.content.userInfo)
switch RevbackChat.shared.handlePush(data: data) {
case is PushAction.ShowMessage, is PushAction.TestPush: completionHandler([.banner, .list, .sound])
case is PushAction.NotOurs: /* свои пуши */ completionHandler([.banner, .sound])
default: completionHandler([])
}
// didReceive — тап по уведомлению открывает чат
if RevbackChat.shared.conversationId(data: data) != nil { RevbackChatUI.present(over: rootViewController) }
// хелпер: наши data-ключи лежат строками на верхнем уровне userInfo; aps и не-строки отбрасываем
func stringData(_ userInfo: [AnyHashable: Any]) -> [String: String] {
var out: [String: String] = [:]
for (k, v) in userInfo { if let k = k as? String, let v = v as? String { out[k] = v } }
return out
}Строки пуша в главном бандле (iOS)
Четыре ключа в Localizable.strings приложения:
"revback.push.visitor.new_message.title_brand" = "%@";
"revback.push.visitor.new_message.body_contentless" = "Новое сообщение от поддержки";
"revback.push.visitor.test.title" = "Тест уведомлений";
"revback.push.visitor.test.body" = "Пуши из чата поддержки доходят";APNs резолвит loc-key по главному бандлу: отсутствующий ключ iOS показывает буквально.
Шаг 5. Ключ APNs для iPhone
В консоли Firebase обязателен APNs Auth Key (.p8): Project settings → Cloud Messaging.
Без него FCM отвечает THIRD_PARTY_AUTH_ERROR, а проверка ключа в кабинете этого не видит.
К нам .p8 загружать не нужно.
Файл сервисного аккаунта Firebase (JSON) в кабинет загружает владелец канала — строка «Ключ Firebase» в настройках канала. Разработчику достаточно знать: без него пуши не уйдут.
Шаг 6. Проверка — тест уведомлений
У SDK есть RevbackChat.debugInfo — версия, ключ, visitorId, installId,
последний токен, lastTokenSync. И RevbackChat.sendTestPush() — сквозной тест
доставки на это устройство.
Кнопки теста в SDK нет: sendTestPush() вызывает само приложение — например,
с отладочного экрана.
val result = RevbackChat.sendTestPush() // suspendlet result = try await RevbackChat.shared.sendTestPush()Что должно получиться. PUT push-token при старте → push-test → уведомление на экране блокировки.
Повтор в течение минуты → 429 и Retry-After: это норма, троттл.
Под капотом: POST /v1/widget/{key}/push-test, троттл 60 секунд.
Шаг 7. Исходы теста
Что вернул sendTestPush() и что с этим делать.
| Результат | Что случилось | Что делать |
|---|---|---|
| Accepted | 202, конверт ушёл в FCM | смотреть шторку устройства |
| Rejectedreason = WidgetKeyMismatch | ключ в конфиге SDK не совпадает с инстансом (и после пересборки сессии) | проверить widgetKey |
| Rejectedreason = PushUnsupported | тип канала без визиторских пушей | канал в кабинете должен быть «Чат в приложении» |
| Rejectedreason = ChannelInactive | канал на паузе или заморожен биллингом | снять паузу в кабинете, SDK ни при чём |
| Rejectedreason = NoTokenRegistered | для этой установки нет подписки | дождаться lastTokenSync.registered == true |
| Rejectedreason = TokenGone | FCM больше не знает токен | перезапросить у Firebase, setPushToken |
| Rejectedreason = RejectedByFcm( |
SENDER_ID_MISMATCH — токен из другого Firebase-проекта; THIRD_PARTY_AUTH_ERROR — нет APNs-ключа |
сверить google-services.json / GoogleService-Info.plist, залить .p8 |
| Rejectedreason = Unknown( |
новый код бэка, SDK его ещё не знает | показать detail как есть |
| TooManyRequests( |
429, троттл | подождать |
| PushUnavailable( |
503: push unavailable: no_credentials (JSON не загружен) или push unavailable: push_disabled |
чинится в кабинете |
| Failed( |
нет сессии/сети, 404 (канал удалён), 502 (Google недоступен — повторить) | по тексту |
Дополнительно
shared_prefs/revback_chat_visitor.xml — включите его в свои backup-rules,
иначе переустановка = новый визитёр и потерянная переписка.