Tutorial hero
Lesson icon

An Early Look at Capacitor (A New Native Bridge for Web Apps)

Originally published February 07, 2018 Time 12 mins

A short while ago, the Ionic team announced that they were working on a project called “Avocado” which later took on its official moniker of Capacitor. In this article, I want to introduce you to Capacitor and what it will do. I will get into the specifics soon (and we will build an example as well), but for now, it is easiest to think of Capacitor as the Ionic team’s own version of Cordova.

Capacitor is not only for Ionic applications. All you need to do to make Capacitor work with an existing project is provide it with the path to the web code that you want to be bundled into the native application, so it could work with just about anything. The philosophy of Capacitor seems to be to easily integrate with whatever build setup you already have, rather than having to integrate your existing projects into Capacitor.

What is Capacitor?

If you signed up for updates from Capacitor then you may have received an email from Max Lynch (Co-founder, Ionic) detailing what it is all about. For any official word on what Capacitor’s goals are, you should listen to what Max and the Ionic team have to say – I expect they will release a blog post when the public alpha is officially launched. The following will be my take on the role and purpose of Capacitor.

As I mentioned before, you can consider Capacitor to be the Ionic team’s own version of Cordova. If you were to use Capacitor, you would be using it instead of Cordova to add native functionality to your web-based mobile applications. Like Cordova, Capacitor will be the bridge between the browser and the device. I think that the Capacitor website describes its goals quite succinctly: Native Progressive Web Apps. Capacitor wants to be a runtime layer that allows you to build an application and have it run anywhere (even if you are integrating native code).

The creation of Capacitor does not mean that anything is happening to Cordova. Cordova is a project run by the Apache Software Foundation – it is its own entity and will continue to exist and function as it always has.

Of course, the Ionic team is not designing their own version of Cordova just for the thrill of it. Capacitor has many worthwhile goals, and will eventually become the default over Cordova for Ionic applications. However, the Ionic team has stated that they will continue to support Cordova. One thing I think is worthwhile pointing out is this statement from Max in one of the update emails for Capacitor:

Our hesitation to jump into this project was driven primarily by not wanting to seem like we were abandoning or throwing out Cordova and Cordova’s technology approach. Rather, we want to build on it and improve it. We hope that comes across. - Max Lynch

The Ionic team could have instead decided to build the features they wanted into Cordova, rather than creating a new native bridge, but ultimately decided that they would need full control of the project to best execute their vision. Making large sweeping changes to a well established and mature framework is not an easy task.

I can see how some people may see this move as a snub to Cordova, especially to those who have put a lot of effort into that ecosystem. Hopefully, this move will not be seen as a competitive or vindictive one, but rather a move that the Ionic team felt necessary to make real strides towards the goal of one codebase, all platforms.

What will Capacitor do differently to Cordova?

The biggest source of friction when developing cross platform applications using web technology is often the native bridge between the browser and the device, which is a role that Cordova has typically filled. This is the crux of the whole “hybrid” approach to building mobile applications, so it makes sense that this is where the friction is often found. It also makes sense that this is then an area with huge potential for improvement.

So, what does the Ionic team plan on doing to improve this? Here are a few things that stand out to me in particular:

PWA Compatibility. There has been a huge movement for Progressive Web Applications recently. They are starting to receive more support from browsers, and more tools are starting to pop up to help people build PWAs. One cool thing about a PWA is that it can also easily be bundled as a native application and access native functionality (using Cordova/Capacitor). Native code will not work when running as a PWA through the browser, though. This can make maintaining a single codebase for a project that will be distributed as a native application and as a PWA (and perhaps even as a desktop application as well) difficult. With Capacitor as the runtime layer, it will be able to gracefully handle any error that arises from attempting to access native functionality in an environment that does not have it.

Ease of integrating native controls. Capacitor is aiming to make it easier to include native user interface controls wherever desired. For example, if you wanted to use a native menu rather than using the browser to create that interface.

Native functionality available immediately. When using Cordova, you need to wait until the device is ready before making calls to native functionality (e.g. by using platform.ready()). Capacitor will export JavaScript on app boot so that this is no longer required.

Plugin support. All native functionality is accessed through a plugin. If you want to use the Camera, then you use the camera plugin. If there isn’t a plugin available to do what you want, or there is but it is not being maintained, you are somewhat out of luck unless you know how to build the plugin yourself. Capacitor is aiming to make the process of creating and maintaining plugins easier.

Redesigned plugins. Capacitor will include a core set of plugins (Camera, File API, Geolocation, etc.) that will be maintained by the Ionic team. They plan on redesigning these plugins to solve some common issues (like the difficulty of using the File API, and the way Android Geolocation works).

Localised installation. Capacitor will be installed locally in projects, meaning that it will be easier to maintain multiple different versions between multiple projects.

Although Capacitor operates quite differently to Cordova, the Ionic team are still aiming for backward compatibility with as many Cordova plugins as possible.

Adding Capacitor to an Ionic Application

Now that we’ve discussed what Capacitor is, let’s build something! You can find a Capacitor start project here. You can clone this and set up a Capacitor project that way, however, we are going to be adding Capacitor to an existing Ionic project (I will assume you are already familiar with creating Ionic projects). We will just generate a blank Ionic project, and add a simple call to the Camera plugin.

Capacitor relies on using the npx command, which is only available in version 8.6.0 or higher of Node. You likely already have Node installed, so the easiest way to switch between Node versions is to install n:

npm install -g n

You can then switch to any Node version you like (you may want to make note of your existing Node environment in case you have projects that rely on it). To switch to version 8.6.0 you would just run:

n 8.6.0

If you plan on building for iOS, you will also need Cocoapods installed (a dependency manager for iOS projects). If you do not already have it installed, you can just run the following command:

sudo gem install cocoapods

1. Generate a New Ionic Application

We are just going to use a blank Ionic project and add Capacitor to it:

ionic start capacitor-test blank

When asked, do not integrate Cordova with the project. Once it has finished installing, make it your working directory:

cd capacitor-test

2. Add Capacitor to the Project

Eventually, Capacitor will be integrated with the Ionic CLI by default, so you wouldn’t have to bother with these set up steps. For now, it is still easy enough to set up Capacitor. First, we will need to add a file called capacitor.config.json to the root folder of the project:

capacitor.config.json

{
  "bundledWebRuntime": true,
  "webDir": "www"
}

It is important that you add the webDir property because this is where Capacitor will look for the web code to bundle into the application – by default, it is set to “public”. You will also need to add the following dependencies to package.json (do not remove the existing dependencies):

"dependencies": {
    //...snip
    "@capacitor/cli": "latest",
    "@capacitor/core": "latest"
  }

and you will need to add the following under "scripts" (again, don’t remove the other entries):

"scripts": {
    //...snip
    "capacitor": "capacitor"
  },

Once you have made these changes, you will need to install the new dependencies by running:

npm install

To add native platforms that you are targeting you can run the following command for iOS:

npx capacitor add ios

or for Android:

npx capacitor add android

You will now see android and ios folders in the root folder of your project. This is all that is required to get Capacitor set up in your application. This does assume that already have the appropriate building environment for iOS/Android set up on your machine (i.e. you have previously been able to successfully create Cordova builds). If you do not have XCode/Android Studio set up on your machine, you can follow this guide by the Ionic team.

3. Use the Camera Plugin

Now we are just going to take a look at a very basic example of integrating the Camera plugin. You can find documentation for the Camera plugin and other plugins here. To access a plugin from Capacitor, all we need to do is import the Plugins from @capacitor/core:

import { Plugins } from '@capacitor/core';

and then we can access the plugin that we are interested in. If we wanted to set up a function that allowed a user to grab a photo from the camera or the user’s photo library, we might do this:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Plugins } from '@capacitor/core';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    constructor(public navCtrl: NavController) {

    }

    grabPhoto() {

        const { Camera } = Plugins;

        Camera.getPhoto({
            quality: 100,
            resultType: 'base64'
        }).then((result) => {
            console.log(result);
        }).catch((err) => {
            console.log(err);
            console.log('Sorry pal, not going to happen');
        });

    }

}

This would grab the photo and return it to us in base64 format. It is important that we include the catch statement here because if we don’t and we attempt to run this application as a PWA we would get the following error:

ERROR Error: Uncaught (in promise): Plugin does not have web implementation.

By adding the catch statement, we can gracefully handle this error without breaking the application:

Handling error when plugin not available

This example will now work seamlessly across multiple platforms. The concept of having plugins not break the web-based implementation of the application is not new, this was something we could do with Ionic Native/Cordova as well, this is just one of the things that the Ionic team wants to make easy with Capacitor.

NOTE: As Max has pointed out in the comments below, the Ionic team do intend to build out web implementations of these plugins. So, in the above example you could actually use this plugin and have it provide you with a nice camera interface even when running as a PWA.

4. Build the Application

We have an application that uses Capacitor, and we have an example implementation of using the Camera plugin, now we just need to deploy it to a device. As I mentioned before, Capacitor works in with whatever your existing environment is. So, in order to produce a production build, you just do whatever it is you normally would.

In the case of Ionic, we can create a production build by running the following command:

npm run ionic:build --prod

This will create a production version of our code in the www directory, which is the directory we told Capacitor to get the web code from earlier. Now you just need to copy this web code into the iOS and Android projects that you created earlier. For iOS you can run:

npx capacitor copy ios

and for Android you can run:

npx capcitor copy android

Then to build the applications, you once again just build it the way you would with a normal iOS/Android project. If you were to run the following command:

npx capcitor open

and choose ios, the application would be launched inside of XCode. From there, you can just hit the play button in the top left to create a build of your application and run it.

Summary

Whilst there already is a lot of codeshare with the way we build Ionic applications today, with the introduction of Capacitor Ionic really seem to be making the one codebase, all platforms dream a literal reality. This project should greatly simplify the process of deploying a single codebase as Desktop/Web/iOS/Android application.

As you can probably tell, this is not some big scary change that is going to completely change the way you build your applications. Once Capacitor is production ready, if you choose to use it, most people will likely be looking at some simple syntax changes.

Learn to build modern Angular apps with my course