Sharing Data between Hybrid App and InAppBrowser

Hi there guys. I came across this requirement in my project where I had to share data between an Ionic app and the InAppBrowser. Though it seemed simple, it turned out to be quite something. So thought I should write this up as a blog. But before we start, I should warn you that this might seem a bit hacky but it is the only thing that works for now.. :P. So let’s get started.

The use case that we’ll be working on is this. The user will open a web page containing a text box in the InAppBrowser. We should get the value entered in the text box back in the app. Simple right. For this example I’ll be using Ionic as the hybrid mobile app development framework. If you haven’t installed Ionic you can learn how to do here. It’s perfectly ok to use just Cordova too. I’ve ignored using angular in this example for the sake of simplicity. If you should choose to use angular, I recommend using ngCordova’s InAppBrowser angular wrapper for the InAppBrowser plugin.

Create a Blank Mobile App

First of all let’s create a blank ionic app.

ionic start InAppBrowser blank

Now we have a blank ionic app. Also make sure to add your required platforms. I’ll be adding android and iOS.

ionic platform add android
ionic platform add ios

Install InAppBrowser

Next, we have to install the InAppBrowser plugin. To do so, type the following command:

cordova plugin add cordova-plugin-inappbrowser

You can find the repository for the plugin here. Now we have added the InAppBrowser plugin.

Understanding InAppBrowser

Before we start with the implementation, let us understand the basics of InAppBrowser. An InAppBrowser allows a mobile application to open web pages within the app without the user having to leave the app. If there was no InAppBrowser the page would open in the mobile’s default browser instead.

Opening a web page in the InAppBrowser is very simple. We just have to call window.open (Ofcourse, after installing the InAppBrowser plugin).

var win = window.open( "http://google.com", "_blank", "location=yes" );
win.addEventListener( "loadstop", function() {
    //code goes here
    win.executeScript({ 
        code: "localStorage.setItem('name', '')" 
    });
});

The first argument is the web page to be opened, the second in this case is _blank, which ensures that the page is always opened in a separate tab. The third is a list of configuration options. We can use these options to do a lot of things, like deciding whether to show the address bar or not and so on. You can have a look at the documentation here.

The important thing to note here is that the window object of an InAppBrowser is not the same as the normal window object. The opener property, the property that contains reference to the window from which the current window was opened, is set to null, which is important here as we cannot use this to store values in the parent window from the InAppBrowser window.

Also, the InAppBrowser does not support postMessage.

It has an addEventListener method, but it only supports the following four events :

  • loadstart – event fires when the InAppBrowser starts to load a URL.
  • loadstop – event fires when the InAppBrowser finishes loading a URL.
  • loaderror – event fires when the InAppBrowser encounters an error when loading a URL.
  • exit – event fires when the InAppBrowser window is closed.

In the above code, we have added a listener to the loadstop event. In the loadstop callback function, we make use of a function called executeScript to execute some code.

executeScript

executeScript is a method that executes javascript in the context of the InAppBrowser. The executeScript method has two parameters. The first parameter is an object which can contain one of the two possible key-value pairs.

code – a string representing the script to be executed
file – a string representing the name of the file containing the script

The second parameter is a callback function, the first argument of which is an array containing the return value of the scripts executed as the first parameter. Consider the following example:

    win.executeScript({
      code: "(function() { return 'sidthesloth' })()"
    },
    function(values) {
        //the first element of the array contains the value returned by the script
        var name = values[0];
    });

When the above code is executed, the value returned by the function will be available as the first element of the argument to the callback function.

That’s all we need to know about InAppBrowser for this demo.

Creating the base template for the App

Having learnt about the InAppBrowser, let us create the base template for our mobile app. Open the index.html file and copy paste the following code:
index.html

<!DOCTYPE html>
<html>
 <head>
 <title>Sharing Data between App and InAppBrowser</title>
 </head>
 <body ng-app="starter">
 <ion-pane>
 <ion-header-bar class="bar-stable">
 <h1 class="title">Ionic Blank Starter</h1>
 </ion-header-bar>
 <ion-content>
    <button type="button" onclick="openInAppBrowser()">Open In App Browser</button>
    <h1 id="output"></h1>
 </ion-content>
 </ion-pane>
 < script type="text/javascript">
    function openInAppBrowser() {
        var win = window.open( "test.html", "_blank", "location=yes" );
    }
 </ script >
 </body>
</html>

All that we have in this page is a button which on being clicked will open the InAppBrowser. We also have an h1 element which will be populated with the value entered in the text box inside the InAppBrowser.

Let us inspect the openInAppBrowser function to see how the InAppBrowser is opened.

Function : openInAppBrowser

var win = window.open( "test.html", "_blank", "location=yes" );

As discussed earlier, the window.open function is used to open the InAppBrowser. We pass in the URL to be opened. I’ve passed in the URL of the test page that we will be opening in the InAppBrowser.

test.html

<!DOCTYPE html>
<html>
<head>
 <title>InAppBrowser - Test Page</title>
</head>
<body ng-app="starter">
 <input type="text" id="nameText" />
 <button onclick="setNameText()">Submit</button>
 < script type="text/javascript">
     function setNameText() {
         var nameText = document.getElementById('nameText').value;
         localStorage.setItem('name', nameText);
         alert('Value set');
     }
 </ script>
</body>
</html>

As you can see, it has a input text box and a button. The button on being clicked will take the value of the name text box and store it in localStorage with the key name.

Why do we need to store it in the localStorage? Because scripts in the InAppBrowser execute in a separate context to that of the mobile app. And since the window object of the InAppBrowser does not support postMessage, the only way we can share data between the two is through localStorage.

So how do we retrieve it on the app? We make use of the executeScript method. First, we need to reset the value of the name field in the localStorage every time the InAppBrowser is opened.

Open index.html and navigate to the openInAppBrowser function. Below the line where you have opened the InAppBrowser window add a ‘loadstop’ event listener.

 win.addEventListener( "loadstop", function() {
     win.executeScript({ code: "localStorage.setItem('name', '')" });
 });

The above code resets the name field in localStorage every time you open the InAppBrowser window.

Next up, we need to read the value from the localStorage of the InAppBrowser inside the app. You might think that all we have to do is to attach an exit listener that reads the value from the localStorage and sets it to the output heading. But the problem with this approach is that, the exit listener will be called only after the InAppBrowser window is closed, which means, the access to the localStorage of the InAppBrowser will be restricted inside the exit callback.

To overcome this, we will create a function that executes at regular intervals of time. This function will get the value of the name field from the localStorage of the InAppBrowser and set it to a variable.

We will make sure that this variable is declared in a function where its scope extends to both the interval function and the exit callback.

function openInAppBrowser() {
    var name;
    var nameInterval;
    var win = window.open( "test.html", "_blank", "location=yes" );
            
    win.addEventListener( "loadstop", function() {
        win.executeScript({ code: "localStorage.setItem('name', '')" });
        nameInterval = setInterval(function() {
            win.executeScript({ code: "localStorage.getItem('name')" }, function(values) {
                name = values[0];
            });
        }, 100)
    });
            
    win.addEventListener('exit', function() {
        clearInterval(nameInterval);
        document.getElementById('output').innerText = name;
    });
}

In the above code, the interval function retrieves the value of the name field from the localStorage of the InAppBrowser and sets it to the name variable declared inside openInAppBrowser function. On closing the InAppBrowser, the exit callback will be called, where in we clear the interval and set the value of name variable to the output heading.

The time interval used for the interval function is totally up to you. Just make sure that the interval is small enough for your requirements.

And that is how you share data between the InAppBrowser and a Hybrid mobile app. Hope this turns out to be useful in your projects too. If you have any queries post them in the comments below…Until next time..Peace..:)

9 thoughts on “Sharing Data between Hybrid App and InAppBrowser

  1. what if I want to you use _system as the second parameter rather than _blank? because in my case I am launching a custom urlScheme e.g (mycoolApp://) rather than a URL

    Like

  2. This works with Ionic running on browser?
    I’m just putting “console.log(‘hello’)” on code property of executeScript and nothing happens on both console (the main window and the new window opened with window.open)

    Ah… Nice tutorial! 😀

    Thanks advance!

    Like

Leave a comment