# Binding Webview to Native App Functions

## Binding Webview clicks to native code

![](/files/-MTFx_421Z61w_tIAdLa)

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](/schema/webview-callback-schema.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.customerglu.com/integration-doc/bind-webview-to-native-app-functions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
