Two-way communication between iOS Webview App and React App

Two-way communication between iOS Webview App and React App

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.

  1. With the click of a button (React JS app) send data to the?IOS App?through WebView
  2. With the click on the alert button (IOS app) send data to the?React JS?through WebView

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>&copy; 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.

Available on?Linkedin,?Github, and?Instagram

要查看或添加评论,请登录

社区洞察

其他会员也浏览了