Two-way communication between iOS Webview App and React App
Atif Qamar
Android | IOS | Flutter | Kotlin | Java | Swift | KMM | RxJava | SwiftUI | Hilt | Mongodb | MySQL | Room Db | Larval | PHP | MVC | MVVM | MVP
Recently I was working on a product where I needed to send hardware data to a React JS app by web view?IOS app?and receive the data from?React JS?app to IOS App.
Preview:
How it works :
UI will be built on the?React JS app?and rendered on?WebView?of the?IOS app.
First, we need to give the bridge name it can be anything. I have given?IOS_BRIDGE.
Second, this communication will work on the event. I have created with the name?iosEvent.
Sending Data from?React JS App?to IOS App:
// Home.js
const onClickHandler = (name) => {
// Sending Data to IOS App
window?.webkit?.messageHandlers?.IOS_BRIDGE?.postMessage({
message: name,
});
}
领英推荐
Receiving Data on IOS App from React JS App :
//MyWebView.swift
extension MyWebView.Coordinator: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "IOS_BRIDGE" {
delegate?.receivedJsonValueFromWebView(value: message.body as! [String : Any?])
}
}
}
Sending Data from IOS App to React JS :
//MyWebView.swift
self.callbackValueFromNative = self.parent.viewModel.callbackValueFromNative
.receive(on: RunLoop.main)
.sink(receiveValue: { value in
let js = "var event = new CustomEvent('iosEvent', { detail: { data: '\(value)'}}); window.dispatchEvent(event);"
webview.evaluateJavaScript(js, completionHandler: { (response, error) in
if let error = error {
print(error)
} else {
print("Successfully sent data React app : \((value))")
}
})
})
Receiving Data on React JS App from IOS App :
// Home.js
const [dataFromIOS, setDataFromIOS] = useState('')
useEffect(() => {
// Adding event for IOS app
window.addEventListener('iosEvent', iosEventHandler);
return () =>
window.removeEventListener('iosEvent', iosEventHandler);
}, [])
const iosEventHandler = useCallback(
(e) => {
alert(e.detail.data);
console.log(e.detail.data);
setDataFromIOS(e.detail.data);
},
[setDataFromIOS]
)
React js complete code :
import React, { useCallback, useEffect, useState } from 'react'
import './Home.css';
const Home = () => {
const [dataFromIOS, setDataFromIOS] = useState('')
useEffect(() => {
// Adding event for IOS app
window.addEventListener('iosEvent', iosEventHandler);
return () =>
window.removeEventListener('iosEvent', iosEventHandler);
}, [])
const iosEventHandler = useCallback(
(e) => {
alert(e.detail.data);
console.log(e.detail.data);
setDataFromIOS(e.detail.data);
},
[setDataFromIOS]
)
const onClickHandler = (name) => {
// Sending Data to IOS App
window?.webkit?.messageHandlers?.IOS_BRIDGE?.postMessage({
message: name,
});
}
const names = ['Atif', 'Jane', 'Vicky', 'Alice', 'Raj'];
return (
<div className="app-container">
<header className="header">
<h1>React iOS Communication</h1>
</header>
<main className="main-content">
<h2>Click on the name to send to the iOS app</h2>
<ul>
{names.map((name) => (
<li key={name} onClick={() => onClickHandler(name)}>
{name}
</li>
))}
</ul>
{dataFromIOS && (
<div className="response">Message From IOS App : {dataFromIOS}</div>
)}
</main>
<footer className="footer">
<p>© 2024 AtifTech</p>
</footer>
</div>
);
}
export default Home
IOS WebView complete code :
// MyWebView.swift
// webview-test
import SwiftUI
import WebKit
import Combine
protocol WebViewHandlerDelegate {
func receivedJsonValueFromWebView(value: [String: Any?])
}
struct MyWebView: UIViewRepresentable, WebViewHandlerDelegate {
// Receiving data from React JS to IOS
func receivedJsonValueFromWebView(value: [String : Any?]) {
print("Received from React app : \(value)")
viewModel.showAlert.send(true)
viewModel.callbackValueFromReactJS.send(value.first?.value as! String)
}
let url: URL?
@ObservedObject var viewModel: WebViewModel
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
print("makeUIView")
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = true
let config = WKWebViewConfiguration()
config.defaultWebpagePreferences = prefs
config.userContentController.add(self.makeCoordinator(), name: "IOS_BRIDGE")
let webview = WKWebView(frame: .zero, configuration: config)
webview.navigationDelegate = context.coordinator
webview.allowsBackForwardNavigationGestures = false
webview.scrollView.isScrollEnabled = true
return webview
}
func updateUIView(_ uiView: WKWebView, context: Context) {
guard let myUrl = url else {
return
}
let request = URLRequest(url: myUrl)
uiView.load(request)
}
class Coordinator : NSObject, WKNavigationDelegate {
var parent: MyWebView
var callbackValueFromNative: AnyCancellable? = nil
var delegate: WebViewHandlerDelegate?
init(_ uiWebView: MyWebView) {
self.parent = uiWebView
self.delegate = parent
}
deinit {
callbackValueFromNative?.cancel()
}
func webView(_ webview: WKWebView, didFinish: WKNavigation!) {
print("webView didFinish")
webview.evaluateJavaScript("document.title") { (response, error) in
if let error = error {
print("title error")
print(error)
}
if let title = response as? String {
self.parent.viewModel.webTitle.send(title)
}
}
// sending data from IOS to React JS
self.callbackValueFromNative = self.parent.viewModel.callbackValueFromNative
.receive(on: RunLoop.main)
.sink(receiveValue: { value in
let js = "var event = new CustomEvent('iosEvent', { detail: { data: '\(value)'}}); window.dispatchEvent(event);"
webview.evaluateJavaScript(js, completionHandler: { (response, error) in
if let error = error {
print(error)
} else {
print("Successfully sent data React app : \((value))")
}
})
})
}
}
}
extension MyWebView.Coordinator: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "IOS_BRIDGE" {
delegate?.receivedJsonValueFromWebView(value: message.body as! [String : Any?])
}
}
}
Complete Code:?https://github.com/geek-atif/IOSReactWebview/
If you have any questions or suggestions, please leave them in the comments.