How to turn a website into a mobile application using 7 JSON strings

Original author: Ethan
  • Transfer
The material, the translation of which we publish today, will focus on the creation of mobile applications based on existing web projects. The author of this article demonstrates tools that allow with minimal effort to develop applications that take advantage of the native capabilities of the iOS and Android platforms and include materials from working sites or local resources. His story begins with the very seven lines of JSON code that allow you to turn sites into mobile applications.

image
Turning a website into a mobile application

Overview


The figure above shows the code that allows you to turn a website into a mobile application. In particular, seven JSON lines highlighted in orange are responsible for the “conversion”. The remaining fragments of the program text describe the capabilities related to the mobile platform on which the application is running.

What if I tell you that in order to use this approach, you do not need to redo the site using a certain framework that brings the look of the resource closer to the look of the mobile application? Moreover, what if the whole development process is to connect the site to a mobile application like the one shown above using a regular URL?

In addition, here is another question: “Is it possible, simply editing JSON, to work with native APIs, with user interface components, to use system transitions between pages?”.

While you are thinking about the answers to these questions, I suggest that you take a look at how the minimal application created using the tools that I want to talk about here looks and works.


Minimal application
Pay attention to how I embedded a page with github.com in this application , however, everything else is native components, such as the top navigation bar and the bottom control panel . At the same time, transitions between the pages of the application use system capabilities. This is done automatically and does not require intervention in the site code.

Before I talk about how this is done, you might have a reasonable question: “All this is good, but is it possible, using the method in question, to create something really useful, and not something like a simple“ viewer "Web pages in the container of the native application?".

Great question. Actually, this article is devoted to the answer to it. In a nutshell, the essence of the methodology discussed here is to create a two-way communication channel between the container for displaying web content and the application. This will give the application the ability to call JavaScript functions located in the container, and the container will allow you to access the native APIs located outside it.

Take a look at an example illustrating the above.


Application for creating QR codes
Here are the main components of this application:

  1. Native navigation header that supports built-in navigation features.
  2. A WebView element into which a web application generating QR codes is embedded.
  3. System component for entering texts at the bottom of the window.

The description of all these elements can be done simply by matching the JSON markup attributes similar to the one shown above.

And finally, pay attention to the fact that the interaction of application components is shown here. Namely, the QR code changes after entering new data. This is done thanks to the ability to call the JavaScript function located inside the web application, which is responsible for creating QR codes based on the data transferred to it.

It should be noted that not one of the frameworks for developing mobile applications has fundamentally tried to solve the problem of “transparent integration of web containers into native applications”, since all of them are either fully focused on the system capabilities of mobile platforms or rely entirely on HTML5.

When talking about the future of mobile applications, it usually revolves around the question of which approach will win: based on HTML5 or on native APIs. What is characteristic, in such reasoning the theme of the coexistence of these two approaches is not raised, and, moreover, the effect of synergy is not considered, which, thanks to the joint use of various technologies, will allow achieving results that are not easy to achieve, relying only on one thing.

In this article I am going to talk about the following things:

  • About why sharing web code and native components is often a good idea.
  • About why the transparent integration of HTML and native components is not an easy task, and how I managed to solve it.
  • And, most importantly, I’ll talk about how you can use the technologies that will be discussed to quickly develop your own applications.

Why use web technology in mobile applications?


Before continuing, let's first talk about whether it’s normal to use the capabilities of HTML and JS in mobile applications, and when a similar approach might come in handy. Here are some situations where mixing web technologies with the native capabilities of mobile platforms might come in handy.

▍1. Using Technologies Created for the Web


For the implementation of some parts of the applications, it may make sense to use web technologies. For example, WebSocket is a web-based technology. To use it, you can use the web engine built-in to the mobile platform ( WKWebView for iOS and WebView Android) instead of installing a third-party library that simply “emulates” WebSocket.

With this approach, you do not need to use additional libraries, it is enough, using standard technologies, to do what you need. This leads us to the next situation.

▍2. Reduce application package sizes


The use of web technologies in mobile applications helps to do what without these technologies would require huge third-party libraries.

For example, in order to embed a QR-code generator in a mobile application, you will need a third-party library that will increase the size of the application package. However, if you use the standard tool for viewing web pages and the JS library connected to the page using a simple design <script> , you can get everything you need without installing third-party libraries.

▍3. Solving the problem of the lack of reliable mobile libraries


Reliable and stable mobile implementations have not yet been created for some of the latest technologies. However, most of these technologies are available in a web environment. As a result, the most effective way to integrate them into mobile applications is to use the appropriate JavaScript library.

▍4. Development of projects using both the capabilities of mobile platforms and web applications


Many novice developers who want to turn their websites into mobile applications find themselves in a difficult situation, finding out that some of the functions of their web projects are too complex to recreate from scratch for each of the target mobile platforms.

For example, one of the pages of a certain site may be too complicated to quickly convert it to the format of a mobile application, while the rest of the content on the site can be easily ported to a mobile application.

In such a case, it would be very helpful if most of the application could be created using the mobile platform, but some particularly complex page could be transparently integrated into the application in its original form.

How it works?


▍A. Jasonette


Jasonette is an open source project aimed at creating cross-platform native applications based on JSON markup.

The Jasonette framework is similar to a web browser, but instead of turning HTML code into web pages, it converts JSON markup into native applications for iOS and Android. The similarity with the browser lies in the fact that any application based on Jasonette has the same base code that interprets different sets of JSON data in the process of creating the final application. The developer does not need to work with the library code. The process of creating an application is to prepare its JSON description, which allows Jasonette, in real time, to convert web pages into native applications.

Read more about Jasonette here . In general, we can say that the Jasonette project is aimed at developing native applications, and here we will talk about how to integrate HTML pages into these native applications.

▍B. Jasonette Web Container


Native mobile applications themselves are great, but sometimes they want to use web technologies, which can be done using standard web containers. However, their integration into applications is not an easy task. Here's what you need to seamlessly integrate web technologies into native applications:

  1. The web container should be a natural part of the mobile app interface. The container should look like any other component of the user interface of the application. Otherwise, it will turn out to be something awkward, looking like what it really is - a website that is shown in a mobile application.
  2. The parent application must be able to control the child web container. With this approach, the application, of which the control is a part to view the contents of a web page, will have convenient means for influencing it.
  3. The child web container must be able to trigger system events in the parent application. The web container embedded in the application must have facilities for accessing the native API.

In fact, implementing all of this requires a lot of effort, so I started with the first paragraph of this list - by simply embedding the web container in the pages of native applications. So I released the first version of JSON Web Container .

This thing turned out to be useful, but it lacked the ability to interact with the parent application and the web container. Namely, the parent application could not control the web container, and the container could not access the parent application. As a result, the container turned out to be completely isolated from its environment as an application.

▍C. Jasonette Web Container 2.0: application-container interaction


After releasing the first version, I experimented with the second part of the above task, which was to allow the container to interact with the parent application.
Let's talk about how I managed to add interactivity to a regular web container, which made it much more powerful and interesting.

Implementation: Interactive Web Container


▍1. URL loading


A task


Earlier, in the first version , in order to use the web container as a component for viewing data, you first had to write it "html" into an attribute $jason.body.background.type , and then, into the attribute $jason.body.background.text , enter the HTML code.

{
  "$jason": {
    "head": {
      ...
    },
    "body": {
      "background": {
        "type": "html",
        "text": "<html><body><h1>Hello World</h1></body></html>"
      }
    }
  }
} 

It is quite natural that it will be more convenient for developers to initialize the container using a regular link to a web page, instead of entering the entire HTML code of the page in one line.

Decision


An attribute has appeared in Web Container 2.0 url . It supports, in particular, local files. By using the design file://... in the container, you can display the file that comes with the application.

{
  "$jason": {
    "head": {
      ...
    },
    "body": {
      "background": {
        "type": "html",
        "url": "file://index.html"
      }
    }
  }
}

Web pages are also output from the Internet in the same way (using the view design http[s]://… ).

{
  "$jason": {
    "head": {
      ...
    },
    "body": {
      "background": {
        "type": "html",
        "url": "https://news.ycombinator.com"
      }
    }
  }
}

▍2. Two way interaction between application and web container


A task


Previously, web containers were used only to display certain content; they could not interact with the main application. This meant that the following scenarios for their use were impossible:

  1. Jasonette impact on web container. Namely, it was impossible to call JavaScript functions located in the container from a Jasonette application.
  2. Container Impact on Jasonette. It was not possible to call native APIs from code located in the container.

The web container could only be used to display pages. This is similar to how elements are embedded in regular web pages iframe when the page cannot access what is inside these elements.

Decision


The goal of the Jasonette project is to create a standard markup language to describe cross-platform mobile applications. In this case, we need a markup language that can accurately describe the interaction between the parent application and the child web container.

In order to achieve this, I used the JSON-RPC technology , which allowed me to establish the interaction of the application and the web container. Since everything in Jasonette is expressed as JSON objects, it was completely natural to use the standard JSON-RPC format as a communication protocol.


Before using JSON-RPC, Jasonette and the web container could not interact. After the implementation of JSON-RPC, two-way communication between the main application and the container became possible.
In order to be able to call JS functions located in the web container, we declare an action $agent.request .

{
  "type": "$agent.request",
  "options": {
    "id": "$webcontainer",
    "method": "login",
    "params": ["username", "password"]
  }
}

$ agent.request is a native API that initiates a JSON-RPC request to a web container. In order to use this mechanism, you need to pass an object options to it as a parameter.

The object options is the JSON-RPC request that will be sent to the web container.

Consider its attributes.

  • id : Web container constructed over the low-level architecture of the agents ( agent ). Usually, view several agents can be associated with one element view , each of them may have a unique identifier (ID). However, a web container is a special type of agent that can only have an identifier $webcontainer , which is why we use this identifier in the request.
  • method : The name of the JavaScript function to call.
  • params : An array of parameters to pass to the JS function being called.

Here is the full code of the described markup:

{
  "$jason": {
    "head": {
      "actions": {
        "$load": {
          "type": "$agent.request",
          "options": {
            "id": "$webcontainer",
            "method": "login",
            "params": ["alice", "1234"]
          }
        }
      }
    },
    "body": {
      "header": {
        "title": "Web Container 2.0"
      },
      "background": {
        "type": "html",
        "url": "file://index.html"
      }
    }
  }
}

Consider the meaning of the code given here.

When loading an element ($ jason.head.actions. $ Load ), you need to execute the JSON-RPC request described in options to the web container agent ( $ agent.request ).

The parameters of the web container are set in $ jason.body.background , in this case the local file is loaded here file://index.html .

During the call to the container, a function will be searched for login , which, when called, thanks params , will be passed two arguments - "alice" and "1234" . It will look like this:

login("alice", "1234")

Here I talked only about how a parent application can call JS functions from a web container, but the reverse process is also possible when the container calls the application to call the native APIs. See the agent documentation for details .

Example


Let's discuss an example of an application for creating QR codes, which you have already seen above.


QR Code Application

  1. The text input
  2. component at the bottom of the window is 100% native.
  3. The QR code is generated by a web application hosted in a web container.
  4. When the user enters some text in the field and presses the button Generate , the action $agent.request of the web container agent Generate is called $agent.request , which leads to the call of the JS function qr .

The code for this application can be found here.

▍3. Script injection


A task


Sometimes you need to dynamically embed JavaScript code into web containers after the HTML source has been loaded into them.

Imagine you want to create your own web browser. To do this, you may need to embed your JS scripts in each web container in order to be able to influence its behavior. Strictly speaking, this is how extensions for web browsers work.

Even if you are not developing a web browser, you may need to use script injection if you need to provide the necessary behavior for pages that you cannot directly control. The only way the mobile app and web container interact is to use the API $agent . But if you cannot change the HTML content, the only way to add an interface $agent to the web container is to dynamically inject the scripts.

Decision


As already mentioned, a web container $jason.body.background is just an agent. This means that when working with it, you can use the same method $agent.inject as when working with ordinary agents.


Embed JS code in a page loaded in a web container

▍4. URL Navigation Handling


Previously, a web container could handle clicks on links in only two ways, being in one of two modes.

  1. In read-only mode, the web container is considered as a read-only element, while all events, such as touching or scrolling, are ignored. All web containers are in read-only state until they are switched to the normal browser mode, as described below.
  2. In the “normal browser” mode, the web container can interact with the page as if we were working with a normal browser. You can enable this mode by writing a action value to the attribute "type": "$default" .

A task


The problem here is that both of the options discussed above are solutions that represent the concept of "all or nothing."

When working in read-only mode, the container ignores all user interactions.

When working in the “normal browser” mode, the web container works, literally, like a browser. When a user touches a link, the page that the link leads to is loaded. At the same time, the developer does not have the opportunity to intercept this event and call, for example, some kind of native API.

Decision


Thanks to the capabilities of the new web container, you can set $jason.body.background an attribute action that contains a description of the mechanism for processing clicks on links.


Action for handling link interactions
Let's look at an example.

{
  "$jason": {
    "head": {
      "actions": {
        "displayBanner": {
          "type": "$util.banner",
          "options": {
            "title": "Clicked",
            "description": "Link {{$jason.url}} clicked!"
          }
        }
      }
    },
    "body": {
      "background": {
        "type": "html",
        "url": "file://index.html",
        "action": {
          "trigger": "displayBanner"
        }
      }
    }
  }
}

Here we attached an action to the web container "trigger": "displayBanner" . This means that when the user clicks on any link displayed on the page displayed in the web container, an action will be triggered displayBanner . However, the web container itself will not process a click on the link.

In addition, if you analyze the event displayBanner , you will notice a variable $jason . In this case, the link that was clicked will be passed to the action through this variable. For example, if you click on the link that contains the address "https://google.com" , the $jason following will appear:

{
  "url": "https://google.com"
}

This means that by analyzing the meaning $jason.url , various actions can be invoked.

Consider another example, which is a web browser implementation.

{
  "$jason": {
    "head": {
      "actions": {
        "handleLink": [{
          "{{#if $jason.url.indexOf('signin') !== -1 }}": {
            "type": "$href",
            "options": {
              "url": "file://key.html"
            }
          }
        }, {
          "{{#else}}": {
            "type": "$default"
          }
        }]
      }
    },
    "body": {
      "background": {
        "type": "html",
        "url": "file://index.html",
        "action": {
          "trigger": "handleLink"
        }
      }
    }
  }
}

Here we check whether the URL contains a string signin , and, depending on the results of the check, we perform various actions.

  1. If the URL contains signin , a native login window opens.
  2. If the URL does not contain this line, the action specified by the parameter is performed "type": "$default" , as a result, our program behaves like a normal browser.

Examples


▍ Web Browser Development


Now we can take advantage of the new Web Container element for application development. Namely, we are talking about the following:

  1. Possibility of standard processing of clicks on links, which corresponds to the behavior of a normal browser.
  2. Ability to handle clicks on links based on their content.

All this gives us the opportunity, for example, to create our own browser by writing just a half dozen lines of JSON code. Since we can now intercept clicks on links, we can analyze $jason.url and perform actions corresponding to different URLs.

Consider an example.


Standard Browser Behavior
Here, the web container behaves like a normal browser ( "type": "$default" ).


Behavior based on the analysis of the $ jason.url parameter
Here, when clicking on a link, a native transition between pages is applied.

This can be achieved by processing various actions based on value $jason.url .

The first step in creating such an application is to attach an action to the web container visit .

{
  ...
  "body": {
    "background": {
      "type": "html",
      "url": "https://news.ycombinator.com",
      "action": {
        "trigger": "visit"
      }
    }
  }
}

The second step is to perform the visit corresponding operation based on the analysis in action $jason.url .

The following shows how we check to see whether there are in $jason.url -line newest , show , ask , and so on (these are links in the top menu). If something like that can be found, we allow the web container to behave like a normal browser by setting a value "type": "$default" .

If it was not possible to find a match for the above template, we make a native transition $href to a new window, passing the URL that the user clicked on as a parameter.

...
"actions": {
  "visit": [
    {
      "{{#if /\\/(newest|show|ask)$/.test($jason.url) }}": {
        "type": "$default"
      }
    },
    {
      "{{#else}}": {
        "type": "$href",
        "options": {
          "url": "https://jasonette.github.io/Jasonpedia/webcontainer/agent/hijack.json",
          "preload": {
            "background": "#ffffff"
          },
          "options": {
            "url": "{{$jason.url}}"
          }
        }
      }
    }
  ]
},

Here you can see the full JSON code for this example, which, incidentally, takes up only 48 lines.

▍ Quick development of a hybrid application


“Hybrid” applications usually mean regular web applications “wrapped” in native mobile applications.

However, here we have in mind something else. In our case, we are talking about real hybrid applications, when the same application can have several native representations and several web representations at the same time. In addition, one representation of such an application can have several native user interface components and a web container, and all this is displayed in one native layout.

The transition between the control that displays web content and standard controls should be so invisible that it would be difficult to detect the border between them.


Hybrid application
In this example, I created an application that displays jasonbase.com in a web container, which acts as the main element for data visualization.

Jasonbase is a free hosting service that I created specifically to host JSON markup for Jasonette-based applications.

In fact, this is a regular site, but I embedded it in a Jasonette application, as a result, when clicking on the links, instead of the usual procedure for opening a web page, a native transition is called $href in the native JASON element.

I did not have to modify the Jasonbase.com code in order to create this application. I simply embed the website in the Jasonette application as a web container and intercepted the clicks on the links to be processed natively, which, in particular, allowed me to use transitions, and, if necessary, I could use other features of the platform on which I’m working Appendix.

The code for this example can be found here .

Summary


I believe that all this works great for the reason that all complex actions are performed at the framework level, and this level is hidden from the developer.

With the usual approach, the developer, in order to achieve a similar result, would have to take on the solution of the following tasks:

  • Embed a web container in a native layout.
  • Creating a JavaScript bridge that allows an application to call functions from a container.
  • Creating an event processing system, thanks to which the container can access the main application by calling native APIs.

The solution to these problems, in the case of Jasonette, was to create an abstraction layer consisting of the following main parts:

  1. A declarative markup language that serves to describe exactly how the web container should be embedded in the native application.
  2. The communication protocol (JSON-RPC), which is extremely simple, allows you to organize the interaction between the parent application and the web container.

I can’t pretend to say that this is the solution to all problems, but I am pleased to note that what I told you here was very useful to me.

I tried to create an application using the latest technology, which did not have a stable and reliable mobile implementation (and it was not clear whether something similar would appear in the foreseeable future due to the features of what I'm talking about). However, to my happiness, there was a JS implementation of this technology, which allowed me to integrate it into the application without any problems.

In general, speaking about the new features of Jasonette, I can say that I really like what happened. Here, by the way, is the project documentation reflecting the innovations.

Now I would like to make one important, in my opinion, remark: "Great power is a great responsibility." The possibilities that the Jasonette project has gained are enormous, therefore, I believe that developers using this framework need to remember the balance of capabilities of mobile platforms and HTML, which allows creating applications that will be convenient and pleasant to work with.

Someone can take Jasonette and create an application based solely on web containers. Such an “application” would more correctly be called a “website”. This approach calls into question the need to create a dedicated application.

I want to draw your attention to the fact that I do not call for always creating applications that necessarily use the capabilities of mobile platforms and HTML pages. I'm just trying to say that such an approach can be useful in many situations. However, it is important not to overdo it, not to cross the boundaries of the rational use of technology.

In the end, I want to say that there are many situations in which the ability to exchange data between the Jasonette base application and the web container allows you to creatively and beautifully solve a variety of problems. It should be noted that here we discussed only the basics. The most interesting is ahead.

Dear readers! Do you plan to use Jasonette to develop cross-platform mobile applications?