Anyone who has attended a presentation on PhoneGap (or Apache Cordova) since its inception has probably heard this famous statement: "The ultimate purpose of PhoneGap is to cease to exist." In 2012, Brian LeRoux wrote up the PhoneGap Beliefs, Goals and Philosophy that stated as much:
This is not some self-defeating far planned suicide, but rather a hope, a desire, to help push the web platform, particularly on mobile devices, forward to the point where tools like PhoneGap would no longer be necessary.
In the past few years, we've certainly seen some huge strides in the right direction. With Google leading the charge in the PWA (progressive web app) space and Apple, slowly, becoming more open about their browser support, the bar of "You may not need PhoneGap/Cordova for this" is getting lower and lower. Add to this the fact that many users are going through "app fatigue" and downloading fewer and fewer apps (and using less of those downloaded), we may be at a tipping point for a swing back to the web site becoming the canonical source for users as opposed to an app.
So where do we stand, from a technology standpoint, in terms of "not needing PhoneGap"?
In this article I'm going to look at the list of core plugins supported by PhoneGap/Cordova and discuss their status in terms of pure web browser support. I'll also touch on a few other items outside that core list as well. Obviously there's more than what I'll be covering here, but the idea is to take stock of where we are, where we're heading, and plan for the future.
The core plugins are:
That's quite a list! In this article I'll cover half of them (up to file transfer) and later this month I'll wrap up the rest.
The battery status plugin allows an app to listen for changes to the amount of charge a device has - specifically when it goes up or down one percent. It also supports events for "critical" and "low" levels.
Fun fact - actually using the battery status listener on Android will drain the battery.
The official spec for battery status is currently at Candidate Recommendation level. CanIUse shows pretty decent on the Android/Firefox side too:
Unfortunately, it turns out there are bad people on the internet (if you're a woman, you probably already know this all too well), and nefarious advertisers were using the Battery API as a way of tracking users. Therefore, Firefox has officially removed support for the API (so in this case, CanIUse is wrong) and while Apple was considering adding it, they are no longer going to support it. (Source: Battery Status API being Removed from Firefox due to Privacy Concerns)
Long story short - while you can use this API in Chrome, I'd be willing to bet it won't last long there either.
The Battery API is useful in cases where you may want to warn a user that their device is about to die and they may want to save their work. I'd use automatic saving (of a draft form perhaps) to help alleviate some of that concern, and consider making the app intelligently retrieve that draft on startup.
Camera support via the web is actually doing very well, thank you very much. I first wrote about this way back in May of 2013 (Capturing camera/picture data without PhoneGap) and discussed how the file/input field of a form could be used to request a device camera. (This also correctly recognized webcams on desktop/laptops.) Back then, this worked well in both Android and Chrome.
I then updated the post in June of last year (Capturing camera/picture data without PhoneGap - An Update). In that post, I discussed how you could request either still pictures or videos. Again, support is really well done here.
This is not a one for one replacement for the Camera plugin. One of the biggest features missing is the ability to ask for a result image of a certain size. This is kind of important since modern mobile devices take pictures with a huge amount of detail. If you allow a user to take multiple pictures and then upload them, you may want to strongly consider adding feedback for the upload process or even having a firm limit on the total number of pictures that can be sent.
But wait…there's more. Earlier this year I blogged about a new API called MediaDevices. My previous two blog entries focused on an HTML tag, input, whereas this API is a pure JavaScript interface for working with media sources on a device. This API provides much deeper integration into the camera, even letting you inspect information about the capabilities of the camera itself.
Right now it is only supported in Chrome, but initial support is coming to Firefox. This API is so new you can't even find it on CanIUse. Use the link to the API, which goes to the Mozilla Developer Network page. At the end is a table that shows compatibility.
As a side note, there's yet another brand new API, Shape Detection API, that will add support for face recognition and bar code scanning right in the browser. This is - well, bleeding, bleeding edge stuff, but it's beginning!
I'd lean on the input/tag version for now. It has great support across browsers and is relatively simple to use. Keep in mind the file sizes for images and consider using MediaDevices on Android devices.
The Console plugin was only created to help patch some issues with certain platforms and the console API (yes, while most people just use console.log, there's actually multiple other methods you can call as well). This all becomes a non-issue if you are building for the web - the console just plain works. However, don't forget you can remote debug for every mobile platform out there. This lets you see console messages (and much more) via dev tools.
Just don't worry about it!
Unlike the other plugins described here, the Device plugin doesn't provide an API. Rather, it simply creates a global variable with the following properties:
The first, device.cordova, simply refers to the version of Cordova used in the app. Obviously that's not needed on the web. Model, platform, and version could be derived by looking at the user agent string, which you can get via navigator.userAgent. Unfortunately, due to years and years of browser vendors hiding their true identities or adding particular strings to make front-end code think they are something else, the string can be a bit hard to parse.
UUID is a unique value for the device, but has different meanings on different platforms. Serial is also device specific. None of these will be available to you.
The isVirtual value simply lets you know if you are running in a simulator. As far as I know, this is not detectable via JavaScript either.
In general, it depends. You can detect iOS by looking for Safari in the user agent (see the source code for the browser version of the Device plugin) and do the same for Android. I'd probably shy away from this and focus more on feature detection. There's always exceptions to that rule too. iOS supported IndexedDB for a while and it was quite a bad implementation.
As for the UUID, you probably only need to worry about identifying the user if your site/app includes some form of login. You could generate your own UUID and store it in client-side storage, but that can be manipulated by the end user. (Am I the only one randomly changes LocalStorage values for fun?)
The Device Motion plugin returns data about the device's accelerometer. Basically, is the device moving, and if so, how much in three dimensions? The Device Orientation plugin tells you what direction the device is pointing.
Both plugin supports getting the current values as well as setting up an interval to return the current values.
The proper spec is a bit more complex. You can listen for a "deviceorientation" and/or "devicemotion" to get similar values, so in order to mimic exactly what the Cordova plugin has, you would need to register/deregister listeners yourself in some timed fashion, or simply listen for the event and ignore the results if you don't care during a certain timeframes.
Support for this is pretty good:
But be sure to read the notes on the CanIUse report about Safari and how it handles values related to true north.
It seems safe to use provided you remember the different API. As long as you aren't plotting the return of astronauts from outer space, it should be more than good enough for web usage. As an aside, a great use of the device motion API is "shake to reload". There is no "shake" event, but you can check if the motion values have changed enough to represent a proper shake event.
I wrote up a simple demo of the device motion API here: Using Device Motion on the Web.
The Dialogs plugin provides access to native-specific user prompts. That probably doesn't sound like a big deal until you see it in action. The built-in JavaScript alert and prompt methods are represented differently, although they aren't quite as ugly as they used to be. Here is an alert:
And here is an example of the Dialog's plugin alert API. It lets you customize the title and button.
Again, this is not terribly different, but in the past, alerts were far uglier in Cordova apps. The difference is more apparent in iOS. Here's the regular alert:
Notice how the filename shows up? The Dialogs version is nice and clean though.
Unfortunately, there is zero access to the native dialogs from JavaScript. As far as I know, there is no API for it either, since "technically" these items exist already, they are just styled differently and have a bit less customization.
As an aside, there is an official specification for "Web Notifications" and a much easier to understand MDN docs. While technically this isn't the same as an alert or confirm native prompt, it is similar enough that you may want to check it out.
Either live with it, or possibly see if your framework has it's own flavor of dialogs. For example, Ionic does - here is an example of their Alert dialog:
Like native dialogs, you're given the choice of custom titles and buttons. While it may not match the system dialog look and feel, it would be consistent through your app and visually pleasing.
The File plugin is a generic wrapper around operations related to the file system of the device. It allows for read and write of both files and directories. The plugin also provides some useful aliases to, for example, the user's media gallery. In general, the plugin "just worked", but had an API that was (at least for this author) a bit difficult to work with. The API was based on the W3C File API spec and to be clear, it wasn't poorly written, just non-trivial to use.
The good news is that you don't have to worry about that API at all. There is zero support for this outside of Chrome, and Google has officially deprecated the API, so you can pretty much consider it non-existent.
While you can't work with the file system, you can store binary data if you encode it in Base64 and store it on the client. I'd probably shy away from using that for large resources, like MP3 tracks, but it is possible. JavaScript has nice APIs for working with Base64 and binary data. It's not necessarily simple, but doable.
Note that the most recent browsers have good support for pure binary data in IndexedDB. Previously this wasn't well supported, but that's improved lately. (Thank you Nolan Lawson for the update!)
The File Transfer plugin is concerned with - wait for it - uploading and downloading of files. It also supports progress events. Luckily, this is absolutely not necessary with XHR2.
For folks who may not know, XHR refers to, generally, AJAX. For years, XHR let you make POST requests to URLs, but it could not include a file as part of the process. XHR2 addressed that. If you check CanIUse for XHR2 support, you can see that it is pretty much universal. This MDN article provides a great example of using the feature.
Just use XHR2. 🙂 It really isn't too difficult to write. If you want an easier API, consider Fetch, which makes AJAX requests even simpler. (Although support is still growing.)
As you can see already, while it's not quite 100% yet, web standards and browser support has come quite a long way since PhoneGap was released. In the next part, I'll continue my way down through the plugin list with more tips and suggestions.
Header image courtesy of Pawel Loj
Raymond Camden is a senior developer advocate for Auth0 Extend. His work focuses on Extend, serverless, and the web in general. He's a published author and presents at conferences and user groups on a variety of topics. Raymond can be reached at his blog (www.raymondcamden.com), @raymondcamden on Twitter, or via email at raymondcamden@gmail.com.