Binding Webview to Native App Functions
Handling the interaction between webview programs and the app
Last updated
Handling the interaction between webview programs and the app
Last updated
The webview might want to access some native features. Like opening a deeplink, showing the share drawer, etc.
callback
native method should be binded with any webview.
The webview will pass eventJSON
to the callback function.
The webview will call this function in the following ways -
app.callback(eventJSON); //Android
webkit.messageHandlers.callback.postMessage(eventJSON); // iOS
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.util.Log;
import org.json.JSONObject;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView)findViewById(R.id.web_view);
webView.loadUrl("https://app-markk.customerglu.com/program"); // webview url
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewJavaScriptInterface(this), "app"); // **IMPORTANT** call it app
}
/*
* JavaScript Interface. Web code can access methods in here
* (as long as they have the @JavascriptInterface annotation)
*/
public class WebViewJavaScriptInterface{
private Context context;
/*
* Need a reference to the context in order to sent a post message
*/
public WebViewJavaScriptInterface(Context context){
this.context = context;
}
/*
* This method can be called from Android. @JavascriptInterface
* required after SDK version 17.
*/
@JavascriptInterface
// **IMPORTANT** call it callback
public void callback(String message){
JSONObject data = new JSONObject(message);
// data has the event information
Log.d(data);
}
}
}
import UIKit
import WebKit
import Foundation
class ViewController: UIViewController , WKScriptMessageHandler{
@IBOutlet weak var mWebKitView: WKWebView!
@IBOutlet weak var mEdtTxt: UITextField!
var mNativeToWebHandler : String = "callback" // **IMPORTANT** call it callback
var webviewUrl: String = "https://app-markk.customerglu.com/program" // webview url
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: webviewUrl)
let myRequest = URLRequest(url: myURL!)
mWebKitView.load(myRequest)
let contentController = WKUserContentController()
mWebKitView.configuration.userContentController = contentController
mWebKitView.configuration.userContentController.add(self, name: mNativeToWebHandler)
}
// message handler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == mNativeToWebHandler {
// message.body holds JSON of the event
let jsonMessageString: String = message.body as? String;
let jsonDict = try JSONSerialization.jsonObject(with: jsonMessageString) as? NSDictionary
// // jsonDict has the event information
print(jsonDict)
}
}
}
package com.customerglu.kotlinwebviewnativecomm
import android.content.Context
import android.os.Bundle
import android.webkit.JavascriptInterface
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
// TODO: Replace url with the url for your program/campaign.
private val url = "<Provide-Your-campaign-url here>"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
exampleWebView.settings.javaScriptEnabled = true
exampleWebView.addJavascriptInterface(JSComm(this),"app")
exampleWebView.loadUrl(url)
}
/**
* Receive message from webview
*/
class JSComm(val context: Context){
@JavascriptInterface
fun callback(message:String){
// Capture message from webview and print as toast
Toast.makeText(context,message, Toast.LENGTH_LONG).show()
}
}
}
import React from "react";
import WebView, { WebViewMessageEvent } from "react-native-webview";
import { StyleSheet, Text, View } from "react-native";
export function GluWebView({ url }: { url: string }) {
/* Callback for the WebView */
function onWebViewEvent(event: WebViewMessageEvent) {
const data: IWebViewEvent = JSON.parse(event.nativeEvent.data);
console.log(data);
}
return (
<View style={styles.container}>
<WebView
source={{ uri: url }}
onMessage={onWebViewEvent} /* Pass the callback (line: 34) defined as props */
renderLoading={() => (
<Text style={styles.text}>Loading Webview...</Text>
)}
renderError={() => (
<Text style={styles.text}>Error Loading Webview...</Text>
)}
startInLoadingState
style={{ marginTop: 20 }}
/>
</View>
);
}
type IWebViewEvent = IOpenDeepLinkEvent | ICloseEvent | IShareEvent;
interface IOpenDeepLinkEvent {
eventName: "OPEN_DEEPLINK";
data: {
deepLink: string;
name: string;
};
}
interface ICloseEvent {
eventName: "CLOSE";
}
interface IShareEvent {
eventName: "SHARE";
data: {
channelName: "WHATSAPP" | "SMS" | "EMAIL" | "OTHERS";
text: string;
image: string;
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "stretch",
justifyContent: "center",
},
text: {
fontSize: 30,
},
});
Flutter WebView 4.0 and above:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
// Import for Android features.
import 'package:webview_flutter_android/webview_flutter_android.dart';
// Import for iOS features.
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
class MyWebView extends StatefulWidget {
@override
WebState createState() => WebState();
}
class WebState extends State<MyWebView> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
// #docregion platform_features
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else {
params = const PlatformWebViewControllerCreationParams();
}
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params);
// #enddocregion platform_features
controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
debugPrint('WebView is loading (progress : $progress%)');
},
onPageStarted: (String url) {
debugPrint('Page started loading: $url');
},
onPageFinished: (String url) {
debugPrint('Page finished loading: $url');
},
onWebResourceError: (WebResourceError error) {
debugPrint('''
Page resource error:
code: ${error.errorCode}
description: ${error.description}
errorType: ${error.errorType}
isForMainFrame: ${error.isForMainFrame}
''');
},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
debugPrint('blocking navigation to ${request.url}');
return NavigationDecision.prevent;
}
debugPrint('allowing navigation to ${request.url}');
return NavigationDecision.navigate;
},
onHttpError: (HttpResponseError error) {
debugPrint('Error occurred on page: ${error.response?.statusCode}');
},
onUrlChange: (UrlChange change) {
debugPrint('url change to ${change.url}');
},
onHttpAuthRequest: (HttpAuthRequest request) {
// openDialog(request);
},
),
)
..addJavaScriptChannel(
'app',
onMessageReceived: (JavaScriptMessage message) {
print("Handle Your navigation logic "+ message.message);
},
)
..loadRequest(Uri.parse('CAMPAIGN_URL'));
// #docregion platform_features
if (controller.platform is AndroidWebViewController) {
AndroidWebViewController.enableDebugging(true);
(controller.platform as AndroidWebViewController)
.setMediaPlaybackRequiresUserGesture(false);
}
// #enddocregion platform_features
_controller = controller;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: true,
child: WebViewWidget(controller: _controller,)
));
}
}
Flutter WebView Below v4.0:
import 'package:webview_flutter/webview_flutter.dart';
class MyWebView extends StatefulWidget {
@override
WebState createState() => WebState();
}
class WebState extends State<MyWebView> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: true,
child: WebView(
initialUrl: "CAMPAIGN_URL", //Need to put Campaign url
javascriptMode: JavascriptMode.unrestricted, //IMPORTANT for enablig javascript
javascriptChannels: <JavascriptChannel>[
JavascriptChannel(
name: 'app', // Name is "app"
onMessageReceived: (s) {
print(s.message); // s.message contains event data like {"eventName":"CLOSE","data":null}
}),
].toSet(),
),
));
}
}
The native callback function should be able to identify the event using eventName
property present in the JSON.
The data
property contains data relevant to that eventName
.
Check out all possible Webview events here.