Would you believe me if I told you that you could turn a website into a mobile app with just seven lines of orange JSON code? You can get the same behavior as a mobile app without having to rewrite your website using some framework API. If you already have an existing website, you can “package” it as a native app by simply referencing the URL.
On top of that, with a little tweaking of the JSON code content, you can directly access all the native apis, native UI components, and native View Transition.
The simplest example looks like this:
As you can see, I’ve embedded a GitHub.com Web page, but the rest of the layout is native UI components, such as the navigation bar and TAB bar at the bottom. We don’t need to rewrite the site using any API, and we can automatically get the native switching effect.
Before explaining how to do this, you might ask, “It looks cool, but what’s the point of this technology beyond presenting Web pages within a native application framework?”
That’s a good question! This is also the focus of this article. All we need to do is create a seamless two-way communication between the Web view and the application, whereby the parent application can trigger any JavaScript function within the Web view, and the Web view can then call the native API from outside.
Such as:
Note that this view contains:
-
Native navigation bar and built-in toggle function
-
A Web view embedded with a Web application that generates a QR code
-
Contains a native text input component at the bottom
All of this is done with a slight adjustment of the properties of the JSON code.
Finally, please note that the QR code will change as you enter different content in the text input area. The input text can trigger the JavaScript function inside the Web application of the QR code generator to regenerate the QR code image.
No development framework has yet tried to fundamentally solve the problem of “seamless integration of Web views with native applications”, because all of these frameworks focus on the fully native, or fully HTML5, approach.
Whenever we hear someone talking about the future of mobile apps, we’re likely to hear something like “Will HTML5 or native methods win out?” “Such a statement.
No one seems to think native and HTML can co-exist, and the synergy and eventual implementation of the two seems not to be easy.
In this article I will introduce:
-
Why blending Web engines with native components is often a better approach.
-
Why seamless HTML and native integration is so difficult, and how to do it.
-
More importantly, how to quickly build your own applications using such technology.
Before going any further, let’s take a look at whether this is good or bad, and when to use it. Some potential uses of this approach are as follows:
1. Use native Web features
It may be more appropriate to use a Web engine for part of your application. WebSocket, for example, is a native Web feature designed primarily for the Web environment. In this case, it is better to use a built-in Web engine (WKWebView for iOS and WebView for Android) rather than install some third-party library that only “emulates” WebSockets.
Wouldn’t it be nice to use a free tool to achieve your goals without installing any extra code? And that leads to the next reason.
2. Avoid large binary files
Some features may require the help of a large third-party library, and you may want to get to them quickly.
For example, in order to include a QR code image generator natively, you may need to install some third-party libraries, which can cause the binaries to grow in size. But if you use a Web view engine and call the JavaScript library through a simple
3. Lack of reliable mobile libraries
For some cutting-edge technologies, a stable and reliable mobile implementation may not be available for the time being.
Fortunately, most of these technologies have Web implementations, so the most efficient way to integrate them is to use JavaScript libraries.
4. Build partially native, partially Web-based applications
Many new developers who want to port their websites to mobile apps are often frustrated or frustrated when they discover that parts of their existing sites are too complex to be quickly rewritten for every mobile platform.
For example, you might have a very complex Web page that can’t be quickly converted to a mobile application, but the rest of the site can be easily converted.
Faced with this situation, it would be nice to somehow build most of the content of your application natively, and for particularly complex pages integrate it seamlessly into your application as HTML.
Jasonette is an open source approach to building cross-platform native applications based on markup languages.
The technology looks like a Web browser, but instead of interpreting HTML markup language as Web pages, it interprets JSON markup as native apps on iOS and Android.
Just as all Web browsers have exactly the same code, but all Jasonette apps have exactly the same library to interpret different types of JSON tags on demand and create your app, as long as you interpret different types of HTML tags on demand. Developers don’t need to touch the code at all, just write tags and “translate” the code into native apps in real time to create their own apps.
While Jasonette’s core role is to build native applications, the focus of this article is on how to integrate HTML into the core native engine.
Native apps are great, but sometimes we still need to use Web functionality.
But integrating Web views with native applications is a messy process. Seamless integration requirements:
-
Web Views should be integrated as part of the native layout: Web views should be incorporated into the application as part of the native layout and should operate in the same way as any other native UI component. Otherwise the user will feel awkward and feel as if they are actually visiting the site.
-
Parent application can control child Web containers: Parent application should be able to control child Web views at will.
-
Child Web container can trigger parent application’s native events: Child application should be able to trigger parent application’s events to run native apis.
This is a lot of work, so I’ll start with the first step: embedding the Web container directly into the native layout – and releasing it as version 1:
JSON Web container, where HTML in JSON becomes a native application component.
This alone is practical enough, but there are limitations due to the inability to interact.
The parent application has no control over the child Web container, which cannot send any event notifications to the parent application, resulting in complete isolation of the Web container from the outside world.
After release 1, we started working on the second issue: adding interactive capabilities to the Web container.
Here’s how you can make the static Web container you created earlier more powerful by adding interactive capabilities.
Before in version 1, in order to use the Web container as a background view component, we first need to $Jason. Body. The background. The type is set to “HTML”, Later in the $Jason. Body. Background. The text property add hardcoded HTML text, such as this:
In general, people prefer to instantiate containers directly using Web urls rather than having the entire HTML code hard-coded as a single line of code.
Web container 2.0 adds url attributes that allow us to embed native HTML in the form of file://, such as this (which can be loaded from native HTML files that accompany application distribution) :
Alternatively, it is possible to embed remote HTTP [s]:// urls, such as this (which can be loaded from remote HTML) :
Previously, the Web container could only be used to present content, not to interact. This means that none of the following is possible:
-
Jasonette to Web container communication: Call JavaScript functions inside the Web container from Jasonette.
-
Communication from the Web container to Jasonette: Invoking the native API from the Web container code.
At this point we can only show the contents of the Web container. This is like the iframe frame embedded in a web page, where the main page has no access to the content in the iframe frame.
Jasonette’s biggest goal is to design a standardized markup language that can describe cross-platform mobile applications. Therefore, we need this markup language to fully describe two-way communication between the parent application and child Web containers.
To do this, I used a JSON-RPC based communication pipeline between the parent application and the child Web container. Since everything in Jasonette is expressed through JSON objects, it seems natural to use the JSON-RPC standard format as the communication protocol.
To enable JavaScript functions to call the Web container, declare an operation called $agent.request:
$agent.request is a native API that triggers jSON-RPC requests to be sent to the Web container. In order to use the API, the Options object must be passed as a parameter.
The Options object is actually a JSON-RPC request to the Web container. The meaning of each attribute is as follows:
-
Id: The Web container is built on top of an underlying architecture called Agents. Typically, you can use multiple agents for a view, and each Agent can have its own unique ID. But the Web container is a special type of Agent that can only use $webContainer as ID, so ID is required here.
-
Method: Name of the JavaScript function to be called.
-
Params: An array of arguments passed to a JavaScript function.
So to be complete, the markup used should look something like this:
This string of markers actually says:
When the view is loaded ($jason.head.actions.$load), a JSON-RPC request ($agent.request) is sent to the Web container Agent, which is specified by options.
The Web container is defined under $jason.body.background, and in this case a local file named file://index.html will be loaded.
It then looks for a JavaScript function called login and passes two parameters under Params (” Alice “and “1234”).
We’ve seen how the parent app triggers JavaScript function calls from the child Web container. We can also do the opposite and have the Web container trigger the parent app’s native API.
See the Agent documentation for details.
The Agent document: https://docs.jasonette.com/agents/
Going back to the QR code generator example introduced above:
-
The text entry component at the bottom is 100% native.
-
The QR code is generated by a Web container running as a Web application.
-
When the user enters content and presses “Generate”, the $Agent. request operation in the Web container Agent is invoked, which in turn invokes the JavaScript function “QR”.
For specific examples, see:
https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/fn/index.json
Sometimes you might want to dynamically inject JavaScript code into the Web container after the initial HTML loading.
Suppose we want to build a custom Web browser application, we might want to inject our own custom JavaScript into each Web view to customize the behavior of the Web view, somewhat like a Web browser extension.
Even if you don’t need to build a Web browser, you still need to use script-injected methods when you want to implement custom behavior for urls that contain content we don’t control. Native applications and Web containers can only communicate through the $Agent API, but if you can’t change the HTML content, you can only add the $Agent interface to the Web container through dynamic injection.
As mentioned above, the Web container $Jason.body. background is also an Agent, which means we can use the exact same $Agent. Inject method as normal agents.
Historically, the Web container could only handle link clicks in two ways:
-
Read-only: Treat the Web container as read-only, ignoring all events such as touch or scroll. All Web containers are read-only at this point, unless explicitly instructed to behave like normal browsers, as described below.
-
Normal browser behavior: Allows users to interact with the page as normal browsers do. To do this, declare “type”: “$default” as the action property.
Both are “All or nothing” solutions.
-
For “read-only,” the Web container ignores all user interactions.
-
For “normal browser behavior,” the Web container will behave the same as the browser. When a link is clicked, the page is refreshed to display the content of the link just like a normal web page, but there is no way to hijack the click and call other native apis.
By using the new Web container, you can attach any action to the $Jason.body. background Web container to handle events like link clicks.
Let’s look at an example:
Here we attach a “trigger” to the Web container: “displayBanner”, which means that when the user clicks on any link in the Web container, the displayBanner action will be triggered instead of being handled directly by the Web view.
Also, if you look at the displayBanner operation, you’ll see that the variable $Jason appears. In this case, the clicked link will be passed through the $Jason variable. For example, if you click on a URL named “https://google.com”, $Jason will get the following values:
This means that we can check the value of $Jason. url and optionally trigger different operations.
Let’s use the implementation of a custom Web browser as another example:
We check to see if the URL contains the string signin and perform two different actions based on the result.
-
If signin is included, open a new view and log in natively.
-
If signin is not included, the “type”: “$default” operation is run directly to achieve the normal browser behavior.
Many interesting things can be done with the following features of the new Web container:
-
Self-loading via URL attributes and acting as a full-featured browser.
-
Selectively handle link clicks depending on the URL.
You can even build a custom Web browser with dozens of lines of JSON code. Since we can now hijack each link click, we can examine $Jason. url and do whatever we need to do based on the result.
For example:
As you can see from the image above, clicking on the link behaves like a normal browser (“type”: “$default”).
As you can see from the figure below, you can switch to another JASON view natively by clicking the link.
All of this can be optionally triggered based on the value of $Jason. url.
Step 1: Append an operation called VISIT to the Web container:
Step 2: According to$jason.url
The value of the operationvisit
Internal related operations
In the following code, we check whether $jason.url is compatible with content of the newest, show, ask, etc. (all top-level menu item links). If so, set “type”: “$default” to make the Web container behave like a normal browser.
If the pattern does not match, a new view can be opened through the native $href transformation and the clicked link is passed as a parameter.
See the full JSON markup for the Web browser (just 48 lines!). :
https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/hijack.json
When people talk about “hybrid” applications, they mostly mean HTML Web applications wrapped inside native application frameworks.
But that’s not what we’re talking about here. By “hybrid” I mean truly hybrid applications, which can contain multiple native views as well as multiple Web-based views at the same time. In this application, a view can have multiple native UI components and a Web container rendered with the same native layout.
The Web view should interweave with the native view as seamlessly as possible, making it completely indistinguishable from the user.
In this example, I created an application that displays the content of JasonBase.com in a Web container as the home page view.
Jasonbase is a free JSON hosting service I developed that can be easily used to host JSON tags used by Jasonette applications.
Of course, this is a website itself, but I’ve embedded it in Jasonette so that clicking on the link doesn’t open the web page, but instead shows the native JASON view through the native $href transformation.
This application can be built without touching the code of Jasonbase.com.
Simply embed a website as a Web container in Jasonette, then hijack the native handling of link clicks, and you can do all sorts of things native apps do, like trigger native apis and do native transformations.
The full code can be seen here:
https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/hybrid.json
What makes this so amazing, in my opinion, is that everything can be handled at the framework level. All the hard work is done in the background.
App developers don’t have to spend a lot of time and effort implementing all of this from scratch themselves:
-
Embed the Web view in the native layout
-
Create a JavaScript bridge so that your application can call functions in the Web view
-
Create a native event handling architecture so that the Web view can trigger native events for the parent application
The overall solution creates an abstraction consisting of the following:
-
Declarative Markup Language: Used to describe how Web views are embedded in native applications.
-
Communication protocol (JSON-RPC) : Used for extremely simple communication between applications and their subWeb views.
I don’t think this approach solves everything, but in my own use case, it’s a good solution, to say the least.
I’m trying to build applications with technology that is so cutting-edge that there is no solid implementation on mobile (due to the nature of the protocol, it’s not even clear that there will be one). Fortunately, all of these technologies are implemented in JavaScript, so they can be easily integrated into your application with little effort.
Overall, the technique is great, and I’m very pleased with the results so far. The latest version of the documentation has included all the new features, you are welcome to explore and try.
Disclaimer: With great power comes great responsibility
I’d like to conclude by saying that while this new technology is really powerful, I think we all need to make more comprehensive trade-offs in terms of user experience when developing apps.
Some people might use this technique to build an app that consists entirely of Web views, but at the end of the day, your app is really just a website, which is the opposite of what it was meant to be.
It’s important to note that I don’t think every application you have should have both HTML and native components. I just think this approach can be useful for many people in specific situations, just don’t overdo it.