# Binding Webview to Native App Functions

## Binding Webview clicks to native code

![](https://1004846827-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0ahLW2WavtwcwNbSR9%2F-MTFxN57qN4aUEueto5Q%2F-MTFx_421Z61w_tIAdLa%2FProduct%20Flow%20Diagram%20-%20v2.0%20-%203.%20CTA%20button.jpg?alt=media\&token=fb5d6347-9cf6-4f0e-aef0-6e2785f619b6)

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 -

```javascript
app.callback(eventJSON); //Android
webkit.messageHandlers.callback.postMessage(eventJSON); // iOS

```

#### Download Sample Kotlin App from our [Github](https://github.com/customerglu/kotlinwebviewnativecomm)

#### Download Sample React Native App from our [Github](https://github.com/customerglu/customerglu-example-react-native)

#### Download Sample Flutter App from our [Github](https://github.com/customerglu/CG-FlutterDemoApp/tree/master)

{% tabs %}
{% tab title="Android(Java)" %}

```java

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);
        }
    }
}

```

{% endtab %}

{% tab title="iOS(Swift)" %}

```swift
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)
        }
    }
}
```

{% endtab %}

{% tab title="Android(Kotlin)" %}

```
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()
        }
    }

}
```

{% endtab %}

{% tab title="React Native" %}

```
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,
  },
});

```

{% endtab %}

{% tab title="Flutter" %}
**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:**&#x20;

```
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(),
      ),
    ));
  }
}
```

{% endtab %}
{% endtabs %}

### EventJSON&#x20;

* 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](https://docs.customerglu.com/schema/webview-callback-schema).
