Mobile apps: the chat in a WebView
How to build reactive.chat into an iOS or Android app – via the embed page /embed/SITE_KEY in a WKWebView or WebView, with identity via JavaScript, file uploads, the back button and push via webhooks.
For apps there is no native SDK. Instead, your app loads a page built specifically for a WebView:
https://reactive.chat/embed/SITE_KEY
The page has no frame, no navigation and no launcher: the widget fills the screen and is open straight away. It is the same chat as on your website – the same project, the same design, the same inbox. You will find the site key in your account under Project → Installation.
Properties of the page:
- only for active projects; otherwise 404,
viewport-fit=coverand safe area – the input field sits above the bar at the bottom edge,- background in your brand colour until the widget is ready,
X-Robots-Tag: noindexandframe-ancestors 'none'– the page belongs in a WebView, not in an<iframe>on another website,- language according to the device language.
iOS (WKWebView)
import WebKit
let konf = WKWebViewConfiguration()
konf.allowsInlineMediaPlayback = true // otherwise the full-screen player pulls the chat away
let web = WKWebView(frame: .zero, configuration: konf)
web.scrollView.bounces = false
web.scrollView.contentInsetAdjustmentBehavior = .never
web.load(URLRequest(url: URL(string: "https://reactive.chat/embed/SITE_KEY")!))
Lay the WebView over the whole area, not into the safe area layout guide – the page accounts for the inset itself.
Android (WebView)
val web = findViewById<WebView>(R.id.chat)
web.settings.javaScriptEnabled = true
web.settings.domStorageEnabled = true // required: no session without it
web.settings.mediaPlaybackRequiresUserGesture = false
web.webViewClient = WebViewClient() // otherwise the system browser opens
web.loadUrl("https://reactive.chat/embed/SITE_KEY")
domStorageEnabled is not optional: the widget stores its IDs in the WebView's storage. Without it, every launch starts a new conversation.
Passing the signed-in user
Never through the address. A hash in a URL ends up in the history, in logs and in Referer headers. Pass the identity via JavaScript after loading. Your server computes the userHash (Identifying visitors) – the secret does not belong in an app package, because apps can be taken apart. Have your app fetch the three values from your server on sign-in.
// Android, in onPageFinished
web.evaluateJavascript(
"""ReactiveChat("identify",{userId:"$id",name:"$name",userHash:"$hash",exp:$exp});""",
null)
// iOS, in webView(_:didFinish:)
web.evaluateJavaScript(
"ReactiveChat(\"identify\",{userId:\"\(id)\",name:\"\(name)\",userHash:\"\(hash)\",exp:\(exp)});")
Make sure to escape values correctly for JavaScript (most easily by serialising them as JSON in your app). An earlier call also works: the ReactiveChat.q queue accepts it before the widget has loaded.
What is different in a WebView
- Push notifications do not come from the chat. If your app should reach the user while the chat is closed, set up a webhook on
message.createdand send the push yourself. - File uploads only work on Android once your app passes on the file chooser (
WebChromeClient.onShowFileChooser); iOS does this by itself. - Back button (Android): the page has no history of its own. Treat the chat like a screen of your app and close it instead of calling
web.goBack(). - Background: do not discard the WebView when the app moves to the background. The widget reconnects by itself after interruptions and sends buffered messages afterwards (Transport).
- Consent: there is no consent platform on the embed page; the widget therefore starts without consent and initially stores its IDs only for the lifetime of the WebView (Privacy integration). Check in your app whether a conversation survives an app restart.
Getting events into the app
You can pass the widget's events on to your app via a JavaScript bridge, for example to set a badge for unread messages:
// Android: register web.addJavascriptInterface(Bruecke(), "App") beforehand
web.evaluateJavascript(
"ReactiveChat('on','unread',function(e){ App.ungelesen(e.count); });", null)
Next: Smart Answer
अंतिम अपडेट: